The problem #
Hiring data is a tower of Babel. A résumé leaves a parser as one shape, lands in an ATS as another, gets re-shaped for a job board, and is flattened again before it ever reaches a model. Every integration is a bespoke mapping, every matching engine reinvents “what is a skill,” and every LLM agent is handed a slightly different blob of JSON.
There’s a good standard for the human résumé — JSON Resume — but nothing canonical for the parts machines now care about: normalized skills, requirement levels, knock-out logic, match scores, model evaluations, and the events that keep two systems in sync.
agent-resume is that missing layer. It’s a small set of versioned JSON Schemas (plus first-class TypeScript types) covering the full lifecycle of an AI-native hiring pipeline — from parsed candidate to scored match to signed webhook.
What you get #
- 📐 Four canonical schemas — candidate, job, match, and sync event. Strict JSON Schema (draft-07),
additionalProperties: false, semver-versioned$ids. - 🔤 TypeScript types that mirror the schemas exactly — no codegen drift, full editor autocomplete.
- ✅ Runtime validators built on Ajv with readable, path-aware errors.
- 🔐 A signed webhook protocol — HMAC-SHA256 envelope with replay protection and a reference signer/verifier.
- 🔁 JSON Resume interop —
ccdmToJsonResume()exports any candidate to the format the rest of the ecosystem already reads. - 📦 Zero-config publish target —
@agent-resume/schemas, ESM + CJS + types, tree-shakeable.
🚀 Quick start #
npm install @agent-resume/schemas
# or
pnpm add @agent-resume/schemas
import {
validateCCDM,
ccdmToJsonResume,
signPayload,
verifyPayload,
type CCDM,
} from "@agent-resume/schemas";
// 1. Validate a parsed candidate against the canonical model.
const candidate = validateCCDM(parsedResume) as CCDM;
// 2. Export it to JSON Resume for any downstream renderer.
const resume = ccdmToJsonResume(candidate);
// 3. Ship a sync event with a tamper-proof signature.
const body = JSON.stringify(syncEvent);
const { header } = signPayload(body, process.env.WEBHOOK_SECRET!);
await fetch(peerUrl, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Agent-Resume-Signature": header },
body,
});
// ...and on the receiving end:
const ok = verifyPayload(rawBody, req.headers["x-agent-resume-signature"], secret);
Need the raw schema for validation in another language?
import { ccdmSchema } from "@agent-resume/schemas";
// or import the JSON directly:
import ccdm from "@agent-resume/schemas/schemas/ccdm";
📐 The four schemas #
| Schema | What it models | Canonical $id |
|---|---|---|
| CCDM — Candidate Canonical Data Model | A structured candidate profile: experience, skills (normalized + proficiency), education, work authorization, preferences, and AI enrichment (embeddings, seniority, generated summaries). | agent-resume.dev/schemas/ccdm/v1.0.0 |
| JD-CDM — Job Description Canonical Data Model | A structured posting: company, comp, responsibilities, and requirements tagged by level — Knock-Out / Required / Preferred / Optional. | agent-resume.dev/schemas/jdcdm/v1.0.0 |
| MatchObject | A candidate↔job match: a deterministic algorithmic score (always present) plus an optional LLM evaluation (recommendation, rationale, requirement-by-requirement analysis). | agent-resume.dev/schemas/match-object/v1.0.0 |
| SyncEvent | An HMAC-authenticated webhook envelope for cross-system sync, covering 18 event types across candidates, jobs, applications, matches, evaluations, and scrapes. | agent-resume.dev/schemas/sync-event/v1.0.0 |
Full field-by-field documentation lives in SPEC.md.
Requirement levels: the core idea behind matching #
JD-CDM tags every requirement with one of four levels. This is what lets a matcher reason instead of keyword-count:
| Level | Meaning | Effect on matching |
|---|---|---|
Knock-Out |
Hard gate (e.g. work authorization, an active clearance). | Failing one sets knock_out_passed: false — the candidate does not advance, regardless of score. |
Required |
Must-have to be competitive. | Heavily weighted; gaps surface as critical. |
Preferred |
Strong plus. | Moderately weighted; raises the ceiling. |
Optional |
Nice to have. | Lightly weighted; tie-breaker only. |
MatchObject: two layers, one object #
The design principle the team settled on: the algorithmic scorer is always present; the LLM evaluation is optional. A match can be produced cheaply and deterministically, then optionally enriched by a model — without ever changing its shape.
{
"schema_version": "1.0.0",
"match_id": "…", "job_id": "…", "candidate_id": "…",
// ── Layer 1: algorithmic scorer (always present) ──
"overall_score": 0.82,
"knock_out_passed": true,
"sub_scores": { "skills": 0.9, "experience": 0.8, "education": 0.75,
"location": 1.0, "compensation": 0.85, "seniority": 0.7 },
"knock_out_results": [{ "requirement": "Authorized to work in US", "passed": true }],
"gaps": [], "strengths": [{ "field": "skill", "note": "SQL exceeds requirement" }],
"scored_at": "2026-06-15T12:00:00Z",
// ── Layer 2: LLM evaluation (null until evaluated) ──
"evaluated_at": "2026-06-15T12:05:00Z",
"evaluator_model": "claude-sonnet-4-5",
"assessment": {
"overallAlignment": { "score": 8, "recommendation": "Yes",
"rationale": "Strong analytics background, light on management." }
/* careerAlignment, skillMatch, will, fit … */
}
}
🔐 Signed sync, by default #
SyncEvent is meant to cross a network boundary, so the standard ships an authentication scheme rather than leaving it to each integrator. Signing follows the well-understood Stripe model so it’s trivial to re-implement anywhere:
signed_payload = "{unix_timestamp}.{raw_request_body}"
signature = HMAC_SHA256(secret, signed_payload) // hex
header = "X-Agent-Resume-Signature: t={ts},v1={sig}"
Verifiers recompute the HMAC, compare in constant time, and reject anything outside a freshness window (default 300s) to defeat replays. The reference implementation (signPayload / verifyPayload) is in the package; the full byte-level spec is in SPEC.md → Sync Protocol.
🔁 How it relates to JSON Resume #
agent-resume is complementary to JSON Resume, not a competitor. JSON Resume is the de-facto standard for the human-facing résumé document; CCDM is a superset built for machines and the rest of the hiring pipeline.
| JSON Resume | agent-resume (CCDM) | |
|---|---|---|
| Primary audience | Humans rendering a résumé | Parsers, ATS, matchers, LLM agents |
| Scope | The résumé document | Candidate + job + match + sync |
| Skills | Free-text name + keywords | Normalized skill, proficiency, years |
| Requirement levels | — | Knock-Out / Required / Preferred / Optional (JD-CDM) |
| Matching & evaluation | — | First-class MatchObject |
| AI enrichment | — | Embeddings, seniority, generated summaries, semantic keywords |
| Work authorization / clearance | — | Structured, matchable |
| Interop | — | ccdmToJsonResume() exports to JSON Resume |
Already have JSON Resume data? CCDM is a strict superset of its candidate fields, so adopting it is additive. Need to render with an existing JSON Resume theme? Call ccdmToJsonResume() and you’re done.
📦 Repository layout #
agent-resume/
├── packages/
│ └── schemas/ # @agent-resume/schemas (the published package)
│ ├── src/
│ │ ├── schemas/ # the 4 canonical JSON Schemas (draft-07)
│ │ ├── types/ # mirror TypeScript types
│ │ ├── adapters/ # ccdmToJsonResume()
│ │ ├── sync/ # HMAC signer / verifier
│ │ └── validate.ts # Ajv validators
│ └── tests/ # vitest — schema + behavior coverage
├── SPEC.md # the formal specification
├── CONTRIBUTING.md
└── examples/ # runnable example documents
This is a pnpm + Turborepo monorepo so the standard can grow additional packages (language bindings, codegen, CLI) without disrupting the schema package.
🛠️ Local development #
pnpm install # Node 22+, pnpm 9+
pnpm build # tsup → ESM + CJS + .d.ts
pnpm test # vitest
pnpm typecheck # tsc --noEmit (strict, exactOptionalPropertyTypes)
🤝 Contributing #
This is meant to be a community standard — proposals, new adapters, and additional language bindings are all welcome. Schema changes follow a strict versioning policy (see SPEC.md → Versioning). Start with CONTRIBUTING.md.
The people who build the standard are credited in CONTRIBUTORS.md — significant contributors are added as their work is merged.
📄 License #
MIT © agent-resume contributors