{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://xcathletes.org/contract/schemas/change-entry.json",
  "title": "change_entry",
  "description": "GET /v1/changes?cursor=: one entry of the change feed (SYNC-01..07). Response schema, PRIN-04 open.",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "seq",
    "op",
    "entity",
    "entity_id",
    "person_id",
    "origin_consumer_id",
    "external_ref",
    "occurred_at"
  ],
  "properties": {
    "seq": {
      "type": "integer",
      "minimum": 1,
      "description": "The server-assigned monotonic sequence; also the cursor a caller resumes from."
    },
    "op": {
      "type": "string",
      "enum": [
        "upsert",
        "delete",
        "retire",
        "scope_change"
      ],
      "description": "SYNC-01. An unrecognized value falls back to treating the entry as a resync signal: re-fetch entity_id through its own GET/list endpoint rather than guessing what changed."
    },
    "entity": {
      "type": "string",
      "enum": [
        "training_session",
        "checkin",
        "person",
        "grant"
      ],
      "description": "An unrecognized value falls back to being ignored (PRIN-03); the feed itself never serves an entity class outside this list."
    },
    "entity_id": {
      "type": "string",
      "description": "A pre-existing platform id (a legacy training_session, checkin, person, or grant predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
    },
    "person_id": {
      "type": "string",
      "description": "A pre-existing platform id (a legacy person predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
    },
    "origin_consumer_id": {
      "type": "string",
      "description": "SYNC-07: the originating team id, never a token id (AUTH-04, so token rotation orphans nothing). A pre-existing platform id (a legacy team predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
    },
    "external_ref": {
      "type": [
        "string",
        "null"
      ],
      "description": "SYNC-07: the caller's own external_ref, null when another team originated the row, when the row carries none, or on a delete entry."
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "revoked_at": {
      "type": "string",
      "format": "date-time",
      "description": "SYNC-02: present only on the one scope_change entry that recorded a grant's end."
    },
    "forwards_to": {
      "type": "string",
      "description": "ENT-09: present only on a retire entry, the person id this record forwards to. A pre-existing platform id (a legacy person predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
    }
  }
}
