Open Job Protocol
A machine-first, vendor-neutral JSON standard for job opportunities — designed for employers, agents, and the agentic talent marketplaces of tomorrow. The counterpart to Open Talent Protocol.
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. It encodes requirements with hard/soft distinctions agents can filter on, compensation and location as structured fields, and hiring process transparency so candidates know what to expect.
| Goal | What it means in practice |
|---|---|
| Machine-first | JSON is the canonical format. Rendering to career pages or PDFs is a downstream concern. |
| Employer-controlled | The visibility section lets employers decide what to share publicly vs. after application. |
| Agent-ready | requirements split into must_have and nice_to_have — agents filter on hard constraints, rank on soft ones. |
| OTP-complementary | Fields map directly to Open Talent Protocol for bilateral matching. |
| 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. |
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 example documents node dist/index.js ../../examples/backend-engineer-senior.json ✓ Valid Open Job Protocol document
Minimal valid document
Only meta, role, organization, requirements.must_have.skills, and offering.location.arrangement are required. Everything else is optional.
{
"$schema": "https://openjobprotocol.org/schema/v0.1.json",
"meta": {
"version": "0.1",
"job_id": "acme-fe-2026-001",
"status": "ACTIVE",
"created_at": "2026-02-21T10:00:00Z",
"updated_at": "2026-02-21T10:00:00Z"
},
"role": {
"title": "Senior Frontend Engineer",
"description": "Build and maintain our React-based product...",
"employment_type": "full_time"
},
"organization": {
"name": "Acme Corp"
},
"requirements": {
"must_have": {
"skills": [
{ "name": "React" },
{ "name": "TypeScript" }
]
}
},
"offering": {
"location": {
"arrangement": "hybrid"
}
}
}
Schema reference
The full schema is a JSON Schema (draft 2020-12) document at
schema/openjob-protocol.schema.json.
Below, each section is documented with its fields, types, and intent.
Identifiers and lifecycle metadata for tracking, deduplication, and expiration across ATS platforms and job boards.
| Property | Type | Req. | Description |
|---|---|---|---|
| version | string | yes | Must be "0.1". Used by parsers to select the correct validation rules. |
| job_id | string | yes | Globally unique job identifier (URI, UUID, or requisition ID). Maps to schema.org identifier. |
| status | enum | yes |
Current posting state:
DRAFTACTIVEPAUSED
CLOSEDEXPIREDFILLED
|
| created_at | date-time | yes | When the posting was first published. Maps to schema.org datePosted. |
| updated_at | date-time | yes | Last modification timestamp. |
| valid_through | date-time | no | Posting expiration date. Maps to schema.org validThrough. |
| source | string | no | Origin ATS or system (e.g. "lever", "greenhouse", "direct"). |
| source_url | uri | no | Canonical URL of the posting. Maps to schema.org url. |
| locale | string | no | BCP 47 language tag. Default: "en". |
The substantive content of the job posting — what the role is and what it involves.
| Property | Type | Req. | Description |
|---|---|---|---|
| title | string | yes | Job title. Maps to schema.org title. |
| description | string | yes | Full job description (markdown). Maps to schema.org description. |
| role_summary | string | no | 1–3 sentence agent-optimized summary for quick screening. |
| function | string | no | Standardized function (e.g. "Engineering", "Sales"). Maps to schema.org occupationalCategory. |
| seniority | enum | no |
internjuniormid
seniorstaffprincipal
leaddirectorvpc_level
|
| employment_type | enum | yes |
full_timepart_timecontract
freelanceinternshiptemporary
Maps to schema.org employmentType.
|
| total_openings | integer | no | Number of positions. Maps to schema.org totalJobOpenings. |
| responsibilities | string[] | no | Key responsibilities (≤7 items recommended). Maps to schema.org responsibilities. |
| work_hours | string | no | Working hours description. Maps to schema.org workHours. |
| job_start_date | date | no | Expected start date. Maps to schema.org jobStartDate. |
| immediate_start | boolean | no | Position available immediately. Maps to schema.org jobImmediateStart. |
The hiring entity. Only name is required within this section.
| Property | Type | Req. | Description |
|---|---|---|---|
| name | string | yes | Company name. Maps to schema.org hiringOrganization.name. |
| url | uri | no | Company website. |
| logo_url | uri | no | Company logo image URL. |
| industry | string | no | Primary industry (NAICS code or free-text). Maps to schema.org industry. |
| size | enum | no |
startupscale_upmid_marketenterprise
|
| department | string | no | Hiring department or division. Maps to schema.org employmentUnit. |
| founded | integer | no | Year founded. |
| headquarters | string | no | HQ location. |
Split into must_have (hard constraints agents MUST filter on) and
nice_to_have (soft preferences agents SHOULD rank on). This distinction
is the key differentiator from schema.org and existing standards.
must_have constraint. This is a hard rule, not a hint.
must_have
| Property | Type | Req. | Description |
|---|---|---|---|
| skills | skill[] | yes | Required skills. Each: name (required), min_years, taxonomy ("esco" | "onet"), taxonomy_id. |
| experience_years | object | no | min and optional max. Maps to schema.org experienceRequirements. |
| credentials | credential[] | no | Required degrees. Each: type, field, level. Maps to schema.org educationRequirements. |
| certifications | string[] | no | Required professional certifications (e.g. "PMP", "AWS CCP"). |
| languages | lang[] | no | Each: language (BCP 47), proficiency:
basicconversationalprofessionalnative
|
| legal.work_authorization | string[] | no | ISO country/region codes. Maps to schema.org eligibilityToWorkRequirement. |
| legal.security_clearance | string | no | Clearance level required. Maps to schema.org securityClearanceRequirement. |
| legal.physical_requirements | string[] | no | Physical demands. Maps to schema.org physicalRequirement. |
nice_to_have
| Property | Type | Req. | Description |
|---|---|---|---|
| skills | skill[] | no | Preferred additional skills (same shape as must_have skills). |
| experience_years | object | no | preferred years of experience. |
| credentials | credential[] | no | Preferred qualifications. Maps to schema.org qualifications. |
| experience_in_place_of_education | boolean | no | Whether experience can substitute for a degree. Maps to schema.org experienceInPlaceOfEducation. |
| preferred_qualifications | string[] | no | Freeform preferred qualifications. |
What the employer provides: compensation, location, benefits, and growth opportunities. At minimum, location.arrangement is required.
offering.compensation
| Property | Type | Req. | Description |
|---|---|---|---|
| salary | object | no | min, max, currency (ISO 4217), period (hourly | daily | weekly | monthly | annual). Maps to schema.org baseSalary. |
| additional_compensation | array | no | Each: type and description. Types:
bonuscommissionequity
tipssigning_bonus
Maps to schema.org incentiveCompensation.
|
| transparency | enum | no |
publicon_requestafter_interview
|
offering.location
| Property | Type | Req. | Description |
|---|---|---|---|
| arrangement | enum | yes |
remotehybridonsite
Maps to schema.org jobLocationType.
|
| primary_location | string | no | Primary work location. Maps to schema.org jobLocation. |
| alternate_locations | string[] | no | Additional office locations. |
| remote_regions | string[] | no | Eligible remote regions (ISO codes). Maps to schema.org applicantLocationRequirements. |
| relocation_support | boolean | no | Whether relocation assistance is offered. |
| visa_sponsorship | boolean | no | Whether visa sponsorship is available. |
offering.benefits
| Property | Type | Req. | Description |
|---|---|---|---|
| benefits | benefit[] | no | Each: category and detail. Categories:
healthretirementdevelopment
flexibilityfamilyfinancialother
Maps to schema.org jobBenefits.
|
offering.growth
| Property | Type | Req. | Description |
|---|---|---|---|
| career_path | string | no | Career progression description. |
| mentorship | boolean | no | Mentorship availability. |
| promotion_cadence | string | no | Review/promotion cycle. |
Team composition and context. No equivalent in schema.org — this is a key OJP differentiator for culture matching.
| Property | Type | Req. | Description |
|---|---|---|---|
| name | string | no | Team or squad name. |
| size | integer | no | Current team size. |
| reports_to | string | no | Hiring manager title. |
| tech_stack | string[] | no | Team's technology stack. |
| methodology | string | no | Work methodology (e.g. "scrum", "kanban"). |
| description | string | no | Team mission and purpose. |
Hiring process transparency — stages, timeline, and application method. Candidates and agents can use this to set expectations.
| Property | Type | Req. | Description |
|---|---|---|---|
| stages | stage[] | no | Each: name, duration_minutes, type ("async" | "call" | "video" | "onsite" | "take_home"). |
| total_duration_days | integer | no | Target time-to-offer in days. |
| decision_timeline | string | no | When candidates hear back after final round. |
| application_url | uri | no | Where to apply. Maps to schema.org url. |
| direct_apply | boolean | no | Whether the URL enables direct application. Maps to schema.org directApply. |
| accepts_otp_profile | boolean | no | Whether OTP profiles are accepted as applications. |
| ai_screening | boolean | no | Transparency: is AI used in candidate screening? |
Culture signals for work-style and values matching.
| Property | Type | Req. | Description |
|---|---|---|---|
| values | string[] | no | Core company values. |
| work_style | string[] | no | Work style descriptors (e.g. "async-first", "low-meeting"). |
| special_commitments | string[] | no | Social/environmental commitments. Maps to schema.org specialCommitments. |
| dei_statement_url | uri | no | DEI statement link. |
| eeo_statement | string | no | Equal employment opportunity statement. |
| diversity_statement | string | no | Diversity commitment text. |
| employer_overview | string | no | Brief employer branding text. Maps to schema.org employerOverview. |
Privacy controls governing what information is publicly visible versus restricted to specific stages of the hiring process.
| Property | Type | Req. | Description |
|---|---|---|---|
| salary | enum | no |
publicon_requestafter_interview
|
| team_details | enum | no |
publicafter_application
|
| process_stages | enum | no |
publicafter_application
|
| hiring_manager | enum | no |
publicafter_applicationafter_interview
|
"visibility": { "salary": "public", "team_details": "public", "process_stages": "public", "hiring_manager": "after_application" }
A free extension object for domain-specific or proprietary fields. Use namespaced keys to avoid collisions with future spec additions.
"extensions": { "jobgrow:match_score": 87, "myats:requisition_id": "REQ-2026-0421" }
Adding data to extensions never breaks a conforming parser. Removing extensions never breaks core functionality.
OTP ↔ OJP bilateral matching
The killer feature: symmetric matching between Open Talent Protocol candidate profiles and OJP job listings. An agent with both documents can compute a bilateral match score without any natural language processing.
| OTP (Candidate) | OJP (Job) | Match type | Logic |
|---|---|---|---|
| skills[] | requirements.must_have.skills[] | hard filter | Skill name match + min_years ≤ candidate years |
| skills[] | requirements.nice_to_have.skills[] | soft rank | Bonus score for matching preferred skills |
| credentials[] | requirements.must_have.credentials[] | hard filter | Credential type + level match |
| work_history[].duration | requirements.must_have.experience_years.min | hard filter | Sum of durations ≥ min |
| languages[] | requirements.must_have.languages[] | hard filter | Proficiency ≥ required level |
| preferences.salary | offering.compensation.salary | bilateral | candidate.min ≤ job.max AND job.min ≤ candidate.max |
| preferences.location | offering.location | hard filter | Arrangement + region compatibility |
| preferences.work_style | culture.work_style | soft rank | Overlap scoring |
| preferences.industry | organization.industry | soft rank | Industry preference match |
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.1", "organization": "Acme Corp", "jobs_url": "https://acme.com/api/jobs.ojp.json", "total_active_jobs": 12, "updated_at": "2026-02-21T14:00:00Z", "accepts_otp_profile": true, "contact": "careers@acme.com" }
Examples
Three complete, schema-conforming documents are included in the repository. They illustrate
how requirements, offering, 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.
Migration
OJP is designed to be clearly mappable to and from existing job posting formats.
| Format | Guide | Notes |
|---|---|---|
| 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. |
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 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.