{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openrolesindex.com/orgs/all/agent/candidate-profile.schema.json",
  "title": "Candidate profile",
  "description": "A job seeker's profile, kept on the candidate's own machine. Claims mirror the ai_requirements shape of the jobs API, so fit is a match requirement by requirement. The same shape at every employer on the platform.",
  "type": "object",
  "required": ["candidate", "claims"],
  "properties": {
    "schema_version": {"type": "integer", "const": 1},
    "candidate_key": {"type": "string", "pattern": "^oric_", "description": "Optional. The candidate key for job alerts at one employer (starts with oric_). Kept locally; never put it in artifacts (résumés, letters, shortlists, commits) or repeat it in chat."},
    "candidate": {
      "type": "object",
      "required": ["locations"],
      "properties": {
        "name": {"type": "string"},
        "contact": {
          "type": "object",
          "description": "Optional. Never sent anywhere by the agent.",
          "properties": {"email": {"type": "string"}, "phone": {"type": "string"}, "url": {"type": "string"}}
        },
        "locations": {
          "type": "array",
          "description": "Where the candidate can work. City/region/country are matched against the job's locations_derived (full names: 'Pennsylvania', not 'PA'). lat/lng with radius_km enables a distance check.",
          "items": {
            "type": "object",
            "properties": {
              "city": {"type": "string"},
              "region": {"type": "string"},
              "country": {"type": "string"},
              "lat": {"type": "number", "minimum": -90, "maximum": 90},
              "lng": {"type": "number", "minimum": -180, "maximum": 180},
              "radius_km": {"type": "number", "minimum": 0}
            }
          }
        },
        "relocation": {"type": "boolean", "description": "true = location is not a hard filter."},
        "work_arrangement": {
          "type": "array",
          "items": {"type": "string", "enum": ["On-site", "Hybrid", "Remote OK", "Remote Solely"]}
        },
        "employment_types": {
          "type": "array",
          "items": {"type": "string", "enum": ["FULL_TIME", "PART_TIME", "CONTRACTOR", "TEMPORARY", "PER_DIEM", "INTERN", "VOLUNTEER", "OTHER"]}
        },
        "pay_floor": {
          "type": "object",
          "required": ["value", "unit"],
          "properties": {
            "value": {"type": "number", "minimum": 0},
            "unit": {"type": "string", "enum": ["HOUR", "DAY", "WEEK", "MONTH", "YEAR"]},
            "currency": {"type": "string", "description": "ISO 4217, e.g. USD."}
          }
        },
        "schedule": {
          "type": "array",
          "description": "Shifts the candidate will work. Suggested values: day, night, evening, rotating, weekend, weekday; free text is allowed. Not an API field: compared against the posting's own shift/hours wording.",
          "items": {"type": "string"}
        },
        "schedule_notes": {"type": "string"},
        "languages": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["code"],
            "properties": {
              "code": {"type": "string", "description": "ISO 639-1, e.g. en, es."},
              "level": {"type": "integer", "minimum": 1, "maximum": 5}
            }
          }
        }
      }
    },
    "claims": {
      "type": "array",
      "description": "What the candidate can show, one entry per skill, tool, certification, language, education or soft skill.",
      "items": {
        "type": "object",
        "required": ["label", "kind", "verification"],
        "properties": {
          "label": {"type": "string", "minLength": 1},
          "kind": {"type": "string", "enum": ["skill", "tool", "certification", "language", "education", "soft"]},
          "years": {"type": ["number", "null"], "minimum": 0},
          "level": {"type": ["integer", "null"], "minimum": 1, "maximum": 5},
          "verification": {
            "type": "string",
            "enum": ["self_reported", "evidence_linked", "assessed", "verified"],
            "description": "self_reported: from conversation or pasted prose. evidence_linked: taken from a DOCUMENT the candidate provided (a résumé file, a certificate); evidence holds the file and line. assessed: a server-scored assessment (credential_id). verified: a third-party registry confirmed it."
          },
          "evidence": {"type": "string", "description": "Free text or a document reference: the résumé line, the certificate file, the repository."},
          "jurisdiction": {"type": "string", "description": "For a licence: the state or country that issued it (full name), e.g. Florida."},
          "number": {"type": "string", "description": "Licence or certificate number, when the candidate gives it."},
          "expires": {"type": "string", "description": "ISO 8601 date a certification or licence expires."},
          "credential_id": {"type": "string", "description": "When assessed or verified: the signed credential's id."},
          "verified_on": {"type": "string", "description": "ISO 8601 date the claim was assessed or verified."}
        }
      }
    },
    "history": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["title"],
        "properties": {
          "title": {"type": "string"},
          "employer": {"type": "string"},
          "from": {"type": "string", "description": "YYYY-MM"},
          "to": {"type": ["string", "null"], "description": "YYYY-MM, or null for the current role."},
          "summary": {"type": "string"}
        }
      }
    },
    "preferences": {
      "type": "object",
      "properties": {
        "categories": {"type": "array", "items": {"type": "string"}, "description": "source_category values to search first."},
        "exclusions": {"type": "array", "items": {"type": "string"}, "description": "Words that exclude a job when its title or category contains them."},
        "must_haves": {"type": "array", "items": {"type": "string"}}
      }
    },
    "synonyms": {
      "type": "object",
      "description": "Requirement label -> claim labels the candidate accepts as the same thing. A match through this list is reported as a synonym match (the agent's judgement, not the data's).",
      "additionalProperties": {"type": "array", "items": {"type": "string"}}
    }
  }
}
