Two formats. One family tree. If you've spent any time around developer résumés, you've met JSON Resume, the open schema that lives at jsonresume.org and turned a CV into a tidy JSON object years before recruiters started talking about AI screening. It was early. It was right. And it quietly proved that career data belongs in a structured file, not a locked PDF.

cv.json is what happens when you take that idea and ask one more question: how does a machine find the file in the first place? That's the gap this guide is about. Both formats describe the same person. Only one of them tells a crawler, an AI recruiter, or another app exactly where your canonical résumé lives and which version it's looking at.

cv.json keeps every field JSON Resume gave you and adds the plumbing a machine needs to discover, version, and trust the file without a human in the loop.

So this isn't a takedown. JSON Resume is the parent. cv.json is field-compatible by design, currently at version 1.2, MIT-licensed, and the spec sits in the open at github.com/freecvorg/cv-json. Let's walk through where they overlap, where they diverge, and why the divergence matters more in 2026 than it would have in 2016.

Where JSON Resume came from, and why it mattered

JSON Resume started as a community answer to a dumb problem: every résumé builder stored your data in its own walled format, so moving from one tool to another meant retyping your life. The fix was a shared JSON schema. Define basics, work, education, skills, and a handful of other arrays once, and any compliant tool could read or render it.

The design choices were good ones. Plain JSON. Human-readable keys. A theme system so the same data could render as a dozen different visual résumés. It became the de facto open résumé schema for developers, and a generation of CLI tools and registries grew on top of it.

But here's the honest part, and the evidence backs it up: there is no credible evidence of a single dominant open résumé-data standard in broad ATS use. The market is fragmented and system-specific. JSON Resume's real-world employer adoption is limited and not well measured. It won the developer mindshare battle. It never won the hiring-pipeline battle, because hiring pipelines were never the thing it was built to enter.

That's the inheritance cv.json works from. A proven schema, a proven idea, and an unfinished job.

The field-level comparison

Start with what's identical, because most of it is. Both formats use the same core arrays and the same general shape. If you can read a JSON Resume file, you can read a cv.json file. The keys you already know carry straight over.

Here's a trimmed cv.json document so you can see the bones:

{
  "basics": {
    "name": "Ashley Carter",
    "label": "Senior Backend Engineer",
    "email": "[email protected]",
    "location": { "city": "Berlin", "countryCode": "DE" }
  },
  "work": [
    {
      "name": "Northwind Systems",
      "position": "Backend Engineer",
      "startDate": "2022-03",
      "highlights": ["Cut p99 latency on the orders API by moving to a read replica"]
    }
  ],
  "education": [],
  "skills": [{ "name": "Go", "level": "Advanced" }],
  "meta": { "version": "1.2", "canonical": "https://livelink.cv/ashley/cv.json" }
}

Now the divergence. cv.json's full field set is basics, work, education, skills, projects, languages, certificates, plus two that JSON Resume doesn't formalize: availability and meta. Only basics and meta are required. Everything else is optional, which means a valid cv.json file can be tiny.

The two new objects

The availability object is the hiring signal. It carries open-to-work, the roles you want, your preferred work type, and visa status. JSON Resume has no native place for any of that, so people stuff it into a summary string a parser has to guess at. cv.json gives it typed fields a machine can read without natural-language interpretation.

The meta object is the trust signal. It holds version and canonical. The version tells a consumer which spec rules apply. The canonical URL tells it which copy of you is authoritative when six stale forks of your résumé are floating around the web. JSON Resume has an informal meta convention, but cv.json treats it as required and load-bearing.

Only basics and meta are required in cv.json. The whole file can be twenty lines and still be valid, discoverable, and versioned.

What stays exactly the same

It helps to see the unchanged surface area in one place, because the list of identical things is longer than the list of new things. The basics object keeps name, label, email, phone, url, summary, location, and profiles. The work array keeps name, position, startDate, endDate, summary, and highlights. The education array keeps institution, area, studyType, and the date pair. skills, languages, projects, and certificates all keep their JSON Resume shapes too. So a JSON Resume validator that only checks those arrays will mostly pass a cv.json file without complaint. The new bits sit alongside the old bits, not on top of them.

Discovery: the gap that matters most

This is the headline difference. A JSON Resume file is just a file. It can sit at a URL, sure, but nothing about the web page tells a visiting machine "your structured résumé is over here." A human has to know the path. A crawler has to guess.

cv.json closes that with three discovery mechanisms, all open, all standard-web:

  1. A <link rel="alternate"> tag in your portfolio's HTML head that points at the JSON file, the same pattern RSS feeds and translations have used for two decades.
  2. A /.well-known/cv.json manifest, following the RFC 8615 well-known URI convention that powers things like security.txt and ACME certificate challenges.
  3. An X-CV-Version HTTP response header, so a consumer can check the version with a single HEAD request before downloading anything.

Add open CORS on top, and any browser-based tool or AI agent can fetch the file cross-origin without a proxy. That's the quiet part doing the heavy lifting. A JSON Resume file served without open CORS headers can choke a browser fetch. the cv.json spec recommends serving the file with open CORS, which allows it by default.

Why discovery is the whole ballgame

Think about how AI recruiting actually consumes data today. The evidence is clear: modern ATS and AI stacks treat résumés as input documents to be parsed into structured fields, then run matching, ranking, and summarization on the result. The bottleneck isn't reading JSON. Any LLM does that effortlessly. The bottleneck is finding the canonical structured source instead of OCR-ing a PDF.

Here's the discovery markup in practice. One tag in your head:

<link rel="alternate"
      type="application/json"
      href="https://livelink.cv/ashley/cv.json"
      title="cv.json">

So when a tool lands on your portfolio, it doesn't scrape rendered text and hope. It reads one tag, fetches one file, and gets clean typed data. That's the difference between a machine guessing at your career and a machine reading it.

Three doors, on purpose

The redundancy is deliberate, and worth understanding before you ship. The three discovery paths fail in different ways, so having all three means no single point of failure. The <link rel="alternate"> tag only works if the consumer already loaded your HTML page, which is fine for a crawler that started on your portfolio but useless for one that only has your domain. The /.well-known/cv.json path solves that case: a consumer can probe a known fixed path against any domain without touching a single HTML page first, the same way a browser checks for /.well-known/security.txt without being told it exists. And the X-CV-Version header lets a consumer poll for changes cheaply with a HEAD request, so a daily crawler can skip the full download when nothing moved. Pick whichever your host supports. Ship two if you can. Ship all three if you control the server.

Versioning and hiring signals

Two résumés can describe the same person and still disagree. One says you left a job in 2024, a fork says 2025, a third has your old title. Which one is true? Without a canonical pointer and a version, a consumer has no principled way to decide. It picks the one it found first, which is a coin flip.

cv.json's meta.canonical field is the tiebreaker. Every copy points home. A consumer that finds a fork can follow the canonical URL back to the authoritative file and the current meta.version. Today that version is 1.2. When the spec moves, the field moves with it, and old consumers know whether they're looking at rules they understand.

The availability block, expanded

The hiring signals deserve a real example, because this is where cv.json stops being "JSON Resume with extra keys" and starts being a format built for the actual hiring moment:

{
  "availability": {
    "open-to-work": true,
    "roles": ["Senior Backend Engineer", "Staff Engineer"],
    "work-type": ["remote", "hybrid"],
    "visa": { "sponsorship-required": false, "regions": ["EU"] }
  }
}

Why this matters for AI screening specifically: by 2025 and 2026, AI in recruiting stopped being experimental. A 2026 report from Lever frames AI and automation as table stakes in modern ATS evaluation, covering candidate matching, ranking, and summarization. Bullhorn, citing the 2026 GRID Industry Trends Report, says 78% of staffing firms growing revenue by more than 25% have AI tools embedded in their ATS. Note the scope: that figure is about fast-growing staffing firms, not all employers. Still, the direction is unmistakable.

When an AI recruiter is ranking candidates, structured availability is a filter it can apply instantly. Open to work? Right role family? Visa-compatible? In a PDF, those answers hide inside prose and a parser has to infer them. In cv.json, they're typed fields. The format does the inference work up front, once, instead of forcing every consumer to redo it.

Why typed beats prose, concretely

Picture the same fact two ways. In a PDF, a candidate writes "Open to remote roles in the EU, no sponsorship needed." A parser has to read that sentence, decide "remote" is a work-type and not a company name, infer that "no sponsorship needed" maps to a false sponsorship flag, and then hope the next candidate phrased it the same way. They never do. One writes "happy to relocate," another writes "EU work authorization in hand," a third buries it in the cover letter. Every variation is a fresh natural-language guess, and guesses fail at scale.

The cv.json version is "sponsorship-required": false and "work-type": ["remote"]. There's nothing to interpret. A filter checks a boolean and a string array, and it returns the same answer for every candidate who set the same fields. That consistency is the actual product here. Typed data turns a thousand fuzzy sentence-readings into one exact match.

Privacy by default

Here's a design decision that surprises people. In cv.json, contact details are hidden by default. Your name, work history, and skills are public, because that's the point of publishing. But your email and phone don't ship in the open file unless you choose to expose them.

JSON Resume has no such convention. Its basics.email and basics.phone are right there in the open document, which is fine until you realize a public JSON file is a perfect scraping target for spambots and recruiter-spam tools. cv.json's default flips that: discoverable career, protected contact. You opt into exposure rather than opting out of it.

It's a small thing that turns out to matter a lot the first time someone clones your public file. And they will. Public means public.

How cv.json compares to the rest of the field

JSON Resume isn't the only standard in the room, so it's worth placing cv.json against the others honestly. None of these has won broad ATS adoption, and you should be suspicious of anyone who claims otherwise.

  • Europass is a European Commission-supported CV and profile format. Real in Europe, especially for public-sector and academic applications, but not a dominant global ATS interchange standard. Don't infer hiring-system adoption from its existence.
  • HR-Open Standards LER-RS (Learning and Employment Records) is an emerging interoperability standard, useful for credentials and verified records, but specialized rather than mass-market for everyday résumés.
  • schema.org is structured-data markup for discoverability and parsing assistance, the same vocabulary that powers rich search results. It's a markup layer, not a canonical applicant-data file. cv.json can coexist with it cleanly.
  • HR-XML mattered historically for HR system interoperability. Current public evidence doesn't support calling it a mainstream résumé standard in 2026.

So where does cv.json sit? It's the only one of the bunch built around a single self-describing file at a stable public URL with native discovery. JSON Resume gave it the schema. The rest of the field gave it the lesson that a standard without a discovery story stays a niche standard.

A schema tells a machine how to read your data. Discovery tells a machine where to find it. cv.json is the rare format that ships both.

A note on ATS reality

Be careful with the scary numbers. The widely repeated "75% of résumés are rejected by ATS" claim isn't supported by peer-reviewed evidence. The better-supported picture is that ATS platforms rank, sort, and filter, and that employer-set knockout questions do most of the early screening, not a blanket auto-reject rule. In one 2025 recruiter sample, 92% said their ATS does not auto-reject based on résumé content. That's a small interview sample, not a population census, so hold it loosely.

What's defensible: ATS usage is mainstream in enterprise hiring. Commonly cited industry figures suggest nearly all Fortune 500 companies and a large majority of recruiters use one, though those are secondary compilations, not audited census data. And formatting genuinely matters: tables, text boxes, multi-column layouts, and graphics increase the risk of misreading during parsing. A clean JSON file sidesteps that entire failure mode, because there's no layout to misparse. There's just data.

Worth saying plainly: no major ATS vendor reads cv.json out of the box today. cv.json is designed for AI recruiters and ATS to read, and it's built for where hiring is heading, not a claim about where every vendor already is.

Tooling, rendering, and what you actually run

A format is only as useful as the things that read and write it, so it's fair to ask what the day-to-day workflow looks like on each side. JSON Resume has a mature tooling story: a CLI, a hosted registry, and a theme ecosystem where one data file renders as many different visual layouts. That theming model is genuinely good, and it's the part most people remember fondly. You write your data once and pick a look.

cv.json keeps that same separation of data from presentation, because it inherits the schema. The same JSON can drive a printable PDF, a portfolio page, and a machine-read file without you maintaining three copies. The difference is where the emphasis lands. JSON Resume optimized the rendering path, the human-facing output. cv.json optimizes the consumption path, the machine-facing input. Both treat your data as the single source of truth. They just point that truth at different readers.

Validation you can actually run

Validation matters more than it sounds, because a file that looks fine to your eye can still break a strict parser. The practical move is to validate against the schema before you publish, the same way you'd lint code before a commit. A valid cv.json file needs basics and meta present, dates in a consistent YYYY-MM or YYYY-MM-DD form, and arrays that are actually arrays even when they hold one item. Three quick checks catch most real problems:

  1. Confirm meta.version is a string, "1.2", not a number 1.2. JSON treats those differently and a strict consumer may reject the bare number.
  2. Confirm meta.canonical is an absolute https URL, not a relative path. A relative canonical is worse than none, because a fork that copies it points home to the wrong domain.
  3. Confirm every date is zero-padded. "2022-3" is a parsing landmine. "2022-03" is safe.

Worked example: the same engineer, both formats

Talk is cheap, so here's the actual diff. Below is a compact JSON Resume file, the kind you'd export from jsonresume.org today.

{
  "basics": {
    "name": "Ashley Carter",
    "label": "Senior Backend Engineer",
    "email": "[email protected]",
    "phone": "+49 30 1234567",
    "summary": "Backend engineer, open to remote EU roles, no visa needed."
  },
  "work": [
    {
      "name": "Northwind Systems",
      "position": "Backend Engineer",
      "startDate": "2022-03",
      "highlights": ["Cut p99 latency on the orders API by moving to a read replica"]
    }
  ],
  "skills": [{ "name": "Go", "level": "Advanced" }]
}

And here's the cv.json version of the same person. Notice three edits: contact details move out of the public file, the hiring intent that was trapped in the summary string becomes a typed availability object, and a required meta block makes the file discoverable and versioned.

{
  "basics": {
    "name": "Ashley Carter",
    "label": "Senior Backend Engineer",
    "summary": "Backend engineer focused on reliability and API performance."
  },
  "work": [
    {
      "name": "Northwind Systems",
      "position": "Backend Engineer",
      "startDate": "2022-03",
      "highlights": ["Cut p99 latency on the orders API by moving to a read replica"]
    }
  ],
  "skills": [{ "name": "Go", "level": "Advanced" }],
  "availability": {
    "open-to-work": true,
    "roles": ["Senior Backend Engineer", "Staff Engineer"],
    "work-type": ["remote"],
    "visa": { "sponsorship-required": false, "regions": ["EU"] }
  },
  "meta": { "version": "1.2", "canonical": "https://livelink.cv/ashley/cv.json" }
}

Same career. The work history and skills didn't change a character. What changed is that a machine can now find the file, know which version it follows, filter on hiring intent without parsing a sentence, and not harvest a phone number it was never meant to read. That's the whole upgrade in one diff.

Migrating from JSON Resume is nearly free

If you already have a JSON Resume file, you're most of the way done. Because cv.json is field-compatible, your existing arrays drop straight in. Here's the practical path:

  1. Take your existing basics, work, education, skills, projects, languages, and certificates as they are. No renaming.
  2. Add a meta object with version set to "1.2" and canonical set to your file's stable URL. This is the only strictly required addition.
  3. Optionally add an availability object if you're job-hunting, so AI recruiters get your open-to-work status, target roles, work type, and visa needs as typed fields.
  4. Decide what to do about contact details. To follow cv.json convention, keep email and phone out of the public file unless you want them scraped.
  5. Publish the file at a stable path and wire up discovery: the <link rel="alternate"> tag, the /.well-known/cv.json manifest, and ideally the X-CV-Version header. Confirm CORS allows cross-origin reads.

Edge cases worth knowing

A few things trip people up. If your JSON Resume file used custom non-standard keys, those carry over fine as data, but other consumers won't know what to do with them, so don't rely on them for interop. If you maintain multiple résumés (one per target role), give each its own canonical URL rather than reusing one, otherwise the canonical pointer lies. And if you serve the file from a static host that strips custom headers, the X-CV-Version header may not stick. That's okay. The link tag and the well-known manifest still give consumers two ways to find and version the file.

Common mistakes to avoid

The migration is short enough that the failure modes are mostly small and avoidable. The recurring ones:

  • Forgetting the canonical, or pointing it at a temporary URL. A canonical that changes every deploy defeats the purpose. Pick a stable path and keep it stable, even across redesigns.
  • Leaving contact details in by habit. If you exported from JSON Resume, basics.email and basics.phone came along. Removing them is a deliberate choice you have to make, because nothing forces it.
  • Treating availability as set-and-forget. A stale "open-to-work": true long after you took a job is worse than no signal, because an AI recruiter trusts the typed field and acts on it. Update it or remove it when your status changes.
  • Skipping discovery and just hosting the file. A cv.json file with no link tag and no well-known manifest is functionally a JSON Resume file. The discovery layer is the reason to migrate, so don't ship half of it.
  • Validating only against JSON Resume. A JSON Resume validator won't check that meta is present and correct, because JSON Resume doesn't require it. Validate against the cv.json spec for the parts that are new.

The honest summary of the migration: you keep everything JSON Resume gave you, you add a required four-line meta block, and you optionally add hiring signals and discovery. That's it. There's no rewrite, no lock-in, and no proprietary tooling in the path. The spec is MIT-licensed and public.

JSON Resume proved structured career data was the right idea. cv.json takes that same data and makes it findable, versionable, and ready for the machines that are increasingly the first reader of any application. If you've got five minutes, build one free at https://freecv.org/builder, point your portfolio's link tag at it, and let the next AI recruiter read the clean version of your career instead of guessing from a PDF.