Open Job Protocol
A machine-first, vendor-neutral JSON standard for job opportunities — with flat, agent-optimized fields aligned with OTP v0.2 for bilateral matching.
Why Open Job Protocol?
Job postings today are unstructured prose optimized for SEO, not for AI agents. schema.org/JobPosting serves Google, not hiring agents. Every ATS, job board, and company career page uses a different format. Critical signals like salary, remote policy, and team composition are buried in paragraphs or missing entirely.
Open Job Protocol (OJP) fixes this. v0.2 restructures postings into
flat, snake_case top-level fields with strict additionalProperties: false,
direct alignment with OTP v0.2
field names, and ESCO taxonomy support for skills. OJP is the employer half of a bilateral ecosystem —
its companion standard, the
Open Talent Protocol,
handles the candidate and profile side.
| Goal | What it means in practice |
|---|---|
| Machine-first | JSON is the canonical format. Flat top-level fields, snake_case keys, and strict additionalProperties: false for reliable parsing. |
| Employer-controlled | salary_transparency lets employers decide when to disclose compensation. No data is shared without explicit intent. |
| Agent-ready | must_have and nice_to_have are structured objects agents can filter and rank on — not prose to be parsed. |
| OTP-aligned | Field names map directly to OTP v0.2 — salary_band, seniority, skill level, and CEFR language proficiencies match exactly. |
| Vendor-neutral | MIT licensed. No required registry, platform, or ATS. |
| Interoperable | Mappable to schema.org/JobPosting, Google for Jobs, LinkedIn Job Posting API, and HR-XML. Full v0.1 migration guide included. |
Quick start
Validate a document
The validator CLI checks any JSON file against the schema and prints readable errors.
# Install and build the validator git clone https://github.com/neogene-ai/open-job-protocol.git cd open-job-protocol/tools/validator-cli npm install && npm run build # Validate one of the v0.2 example documents node dist/index.js ../../examples/v0.2/backend-engineer-senior.json ✓ Valid Open Job Protocol document
Minimal valid document
A valid v0.2 document needs only the 10 required top-level fields. Everything else — salary, requirements, team, process — is optional.
{
"schema_version": "0.2.0",
"ojp_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created_at": "2026-03-30T10:00:00Z",
"updated_at": "2026-03-30T10:00:00Z",
"status": "active",
"title": "Senior Frontend Engineer",
"description": "Build and maintain our React-based product...",
"employment_type": "full_time",
"organization": { "name": "Acme Corp" },
"location": { "arrangement": "hybrid" }
}
Schema reference
The full schema is a JSON Schema (draft 2020-12) document at
schema/v0.2/ojp.schema.json.
Below, each section is documented with its fields, types, and intent.
Every OJP v0.2 document must include these 10 required fields.
| Property | Type | Req. | Description |
|---|---|---|---|
| schema_version | string | yes | Must be "0.2.0". |
| ojp_id | uuid | yes | Unique job posting identifier. |
| created_at | date-time | yes | Posting creation timestamp (ISO 8601). |
| updated_at | date-time | yes | Last modification timestamp (ISO 8601). |
| status | enum | yes |
Lifecycle status:
draftactivepaused
closedexpiredfilled
|
| title | string | yes | Job title (max 200). |
| description | string | yes | Full role description (max 5000). |
| employment_type | enum | yes |
full_timepart_timecontract
freelanceinternshiptemporary
|
| organization | object | yes | Hiring entity. See organization. |
| location | object | yes | Work location and arrangement. See location. |
Optional top-level fields
| Property | Type | Description |
|---|---|---|
| valid_through | date | Posting expiration date. |
| summary | string | One-paragraph agent-optimized role summary (max 500). |
| seniority | enum |
Matches OTP seniority exactly:
internjuniormid
seniorstafflead
principaldirectorvpc_level
|
| function | string | Organizational function (max 100). E.g. "Engineering", "Sales". |
| total_openings | integer | Number of open positions (min 1). |
| salary_transparency | enum |
When salary is disclosed:
publicon_requestafter_interview
|
The hiring entity. Only name is required within this object.
| Property | Type | Req. | Description |
|---|---|---|---|
| name | string | yes | Company name (max 200). |
| url | uri | no | Company website. |
| industry | string | no | Primary industry (max 100). |
| size | enum | no |
Matches OTP preferences.company_size:
startupscale_upmid_marketenterprise
|
| department | string | no | Hiring department (max 100). |
| headquarters | string | no | HQ location (max 200). |
Work location and arrangement. Only arrangement is required.
| Property | Type | Req. | Description |
|---|---|---|---|
| arrangement | enum | yes |
Matches OTP work_model:
onsitehybridremote
|
| country | string | no | ISO 3166-1 alpha-2. Matches OTP location.country. |
| city | string | no | City name (max 100). |
| region | string | no | State or region (max 100). |
| remote_regions | string[] | no | ISO 3166-1 alpha-2 country codes for remote eligibility. |
| relocation_support | boolean | no | Whether relocation assistance is offered. |
| visa_sponsorship | boolean | no | Whether visa sponsorship is available. |
Compensation range. Same structure as OTP salary_band for bilateral matching.
| Property | Type | Req. | Description |
|---|---|---|---|
| min | number | yes | Minimum salary. |
| max | number | yes | Maximum salary. |
| currency | string | yes | ISO 4217 currency code (e.g. "EUR", "USD"). |
| period | enum | yes |
annualmonthlydailyhourly
|
Hard requirements — candidates must meet all of these.
An OJP-compliant agent must not present a candidate who fails any must_have constraint.
| Property | Type | Description |
|---|---|---|
| skills | skill_requirement[] | Each: name (required), min_level (1–5, maps to OTP skills[].level), min_years, esco_id. |
| experience_years | object | min and optional max. |
| credentials | string[] | Required degrees or qualifications (max 200 each). |
| certifications | string[] | Required certifications (max 200 each). E.g. "CKA", "ACLS". |
| languages | language_requirement[] | Each: language (ISO 639-1), proficiency (CEFR):
A1A2B1B2C1C2native
Matches OTP languages[].proficiency exactly.
|
| work_authorization | string[] | ISO 3166-1 alpha-2 country codes. Matches OTP visa_status.authorized_countries. |
Preferred qualifications — bonus scoring, not hard filters.
| Property | Type | Description |
|---|---|---|
| skills | skill_requirement[] | Same shape as must_have.skills. |
| experience_years | object | preferred years. |
| credentials | string[] | Preferred qualifications. |
| certifications | string[] | Preferred certifications. |
| languages | language_requirement[] | Same shape as must_have.languages. |
must_have / nice_to_have split is the key agent differentiator.
An OJP-compliant agent must treat must_have as a hard filter and nice_to_have as ranking signal.
This is a hard rule, not a hint.
Array of benefits offered by the employer.
| Property | Type | Req. | Description |
|---|---|---|---|
| category | enum | yes |
healthretirementdevelopment
flexibilityfamilyfinancialother
|
| description | string | yes | Benefit details (max 500). |
Team composition and context for culture matching.
| Property | Type | Description |
|---|---|---|
| name | string | Team or squad name (max 100). |
| size | integer | Current team size. |
| reports_to | string | Hiring manager title (max 200). |
| tech_stack | string[] | Team's technology stack (max 100 each). |
| methodology | string | Work methodology (max 100). E.g. "scrum", "kanban". |
| description | string | Team mission and purpose (max 1000). |
Hiring process transparency — stages, timeline, and application method.
| Property | Type | Description |
|---|---|---|
| stages | hiring_stage[] | Each: name, type:
screeningtechnicalbehavioral
case_studytake_homeofferother
and optional duration_minutes.
|
| total_duration_days | integer | Target time-to-offer in days. |
| decision_timeline | string | When candidates hear back (max 200). |
| application_url | uri | Where to apply. |
| accepts_otp_profile | boolean | Whether OTP profiles are accepted as applications. |
| ai_screening | boolean | Transparency: is AI used in candidate screening? |
Culture signals for work-style and values matching.
| Property | Type | Description |
|---|---|---|
| values | string[] | Core company values (max 100 each). |
| work_style | string[] | Work style descriptors (max 100 each). E.g. "async-first", "low-meeting". |
Data provenance — which agent or system created this posting.
| Property | Type | Req. | Description |
|---|---|---|---|
| agent_id | string | yes | Identifier of the agent or system (max 200). |
| platform | string | no | Platform name (max 200). E.g. "lever", "greenhouse". |
OTP ↔ OJP bilateral matching
Symmetric matching between OTP v0.2 candidate profiles and OJP v0.2 job listings. An agent with both documents can compute a bilateral match score without any natural language processing.
| OTP v0.2 (Candidate) | OJP v0.2 (Job) | Match type | Logic |
|---|---|---|---|
| skills[].name / .level | must_have.skills[].name / .min_level | hard filter | Skill name match + level ≥ min_level |
| skills[] | nice_to_have.skills[] | soft rank | Bonus score for matching preferred skills |
| certifications[] | must_have.certifications[] | hard filter | Certification name match |
| experience[].duration | must_have.experience_years.min | hard filter | Sum of durations ≥ min |
| languages[].proficiency | must_have.languages[].proficiency | hard filter | CEFR level ≥ required level |
| salary_band | salary_band | bilateral | candidate.min ≤ job.max AND job.min ≤ candidate.max |
| work_model | location.arrangement | hard filter | Arrangement in candidate's work_model array |
| visa_status.authorized_countries | must_have.work_authorization | hard filter | Candidate authorized in required country |
| seniority | seniority | soft rank | Exact or adjacent match |
| preferences.culture_values | culture.values | soft rank | Overlap scoring |
Discovery
Employers host a well-known discovery file so agents can find all active listings without scraping.
# https://acme.com/.well-known/ojp.json { "version": "0.2.0", "organization": "Acme Corp", "jobs_url": "https://acme.com/api/jobs.ojp.json", "total_active_jobs": 12, "updated_at": "2026-03-28T14:00:00Z", "accepts_otp_profile": true, "contact": "careers@acme.com" }
Examples
Three schema-conforming v0.2 documents are included in the repository. They demonstrate
how must_have/nice_to_have, salary_band, and process
are used across different industries and seniority levels.
Tools
Validator CLI
A Node.js CLI that validates any JSON file against the OJP schema and prints readable errors.
Source: tools/validator-cli/
cd tools/validator-cli npm install && npm run build node dist/index.js path/to/job.json
Agent tools
TypeScript stubs for integrating OJP into MCP-compatible agent frameworks.
Source: tools/agent-examples/
Provides composable functions ready to be registered as MCP tools:
validateJobPosting, matchCandidateToJob, and introspectJobPosting.
MCP server
A standalone MCP server exposing all 6 OTP/OJP agent tools (validate, introspect, parse for both schemas).
Source: tools/mcp-server/
(hosted in the OTP repo).
Migration
OJP is designed to be clearly mappable to and from existing job posting formats.
| Format | Guide | Notes |
|---|---|---|
| OJP v0.1 | v0.2-migration.md | Full field mapping from nested (meta, role, offering) to flat top-level structure. v0.1 schema remains available. |
| schema.org/JobPosting | migration-schemaorg.md | Full field-by-field mapping. OJP is a superset of schema.org’s job properties. |
| HR-XML | migration-hrxml.md | Conceptual guide. HR-XML’s PositionOpening maps to OJP without data loss. |
| LinkedIn Job Posting API | migration-linkedin.md | Maps LinkedIn’s foundation schema fields to OJP sections. |
Ecosystem
OJP defines the employer side of agentic marketplaces. It is designed to work alongside complementary open standards that together cover both sides of the hiring equation.
opentalentprotocol.org · GitHub repository
Together, OTP + OJP enable fully structured, agent-driven bilateral matching. Employers express requirements and offer details in an OJP posting; candidates express preferences and constraints in their OTP profile. Agents on both sides can evaluate fit and negotiate programmatically — no unstructured job-board listings or PDF resumes required.
Governance & neutrality
Open Job Protocol is vendor-neutral. JobGrow and neogene.ai are its initial sponsors, but the spec is designed for broad ecosystem adoption. No single company has veto power over the schema.
OJP is the employer-side counterpart to Open Talent Protocol. Together, they form a complete, open standard for agentic talent marketplaces. The same community develops both protocols.
The project is governed by a maintainer council using lazy consensus. See GOVERNANCE.md for the full process.
Licensed under MIT. The spec will always be free to implement.