{
  "openapi": "3.1.0",
  "info": {
    "title": "XC Log",
    "version": "1.0.0",
    "description": "XC Log, maintained by XC Athletes and served at xcathletes.org: the versioned, stack-neutral HTTP+JSON /v1/ API over athlete training data (PRIN-01). Response schemas are open (additionalProperties permitted, PRIN-04); request schemas are strict. A conforming consumer MUST ignore unknown fields and MUST map an unrecognized enum value to the fallback its own schema description names (PRIN-03).",
    "contact": {
      "name": "XC Athletes",
      "url": "https://xcathletes.org/docs"
    }
  },
  "servers": [
    {
      "url": "https://xcathletes.org",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "capabilities",
      "description": "Public discovery."
    },
    {
      "name": "token",
      "description": "Whoami."
    },
    {
      "name": "people",
      "description": "The athlete entity, creation, and identity (ID-01..08)."
    },
    {
      "name": "training-sessions",
      "description": "Training rows (WR-01..09)."
    },
    {
      "name": "checkins",
      "description": "Daily check-ins (ENT-07)."
    },
    {
      "name": "grants",
      "description": "Team-to-person authorization (GR-01..07)."
    },
    {
      "name": "join-codes",
      "description": "Athlete-generated join-code redemption (ID-02)."
    },
    {
      "name": "person-links",
      "description": "Candidate same-person links (ID-08)."
    },
    {
      "name": "rollups",
      "description": "Per-(person, week, origin team) totals."
    },
    {
      "name": "changes",
      "description": "The ordered sync feed (SYNC-01..07)."
    }
  ],
  "paths": {
    "/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "summary": "Discover deployment capabilities",
        "description": "Public, unauthenticated, CORS-open (LIM-05). Reports which tiers and features this deployment implements.",
        "tags": [
          "capabilities"
        ],
        "x-tier": 0,
        "security": [],
        "responses": {
          "200": {
            "description": "The deployment's tiers and feature flags.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "version",
                    "tiers",
                    "features"
                  ],
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "tiers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "features": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "version": "v1",
                  "tiers": [
                    "reader",
                    "writer",
                    "roster_aware"
                  ],
                  "features": {
                    "export": false,
                    "join_codes": false,
                    "rollups": true,
                    "changes": true
                  }
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/v1/token": {
      "get": {
        "operationId": "getToken",
        "summary": "Whoami: what this bearer token resolves to",
        "description": "Reports the token's kind, tier, team, granted-person count, and issuance/use timestamps. Any tier may call this.",
        "tags": [
          "token"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The token's own summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "kind",
                    "tier",
                    "team",
                    "person_count",
                    "issued_at",
                    "last_used_at"
                  ],
                  "properties": {
                    "kind": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "integer"
                    },
                    "team": {
                      "type": "object"
                    },
                    "person_count": {
                      "type": "integer"
                    },
                    "issued_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "last_used_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "kind": "live",
                  "tier": 3,
                  "team": {
                    "id": "01020304050607080910111213141516",
                    "name": "ECXC"
                  },
                  "person_count": 24,
                  "issued_at": "2026-01-01T00:00:00.000Z",
                  "last_used_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/people": {
      "get": {
        "operationId": "listPeople",
        "summary": "List every person this token is granted on",
        "description": "Tier 1. Every person the caller holds a live-or-ended grant on, in either scope, cursor-paginated.",
        "tags": [
          "people"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of granted people.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "LIM-02: a strong ETag over the page body."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_cursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Person"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "example": {
                  "items": [
                    {
                      "id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "given_name": "Riley",
                      "family_name": "Sundahl",
                      "middle_name": null,
                      "status": "active",
                      "claimed_at": null,
                      "claimed_by_relationship": null,
                      "links": [],
                      "created_at": "2026-08-01T12:00:00.000Z",
                      "updated_at": "2026-08-01T12:00:00.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "operationId": "createPerson",
        "summary": "Create a person record",
        "description": "Tier 3 (ID-01). Requires the FLOOR-01 attestation and a birth date hashed and immediately discarded (ENT-04). Writes the creating team's own provisional, write-both-scopes grant (GR-03/GR-07).",
        "tags": [
          "people"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PersonCreateRequest"
              },
              "example": {
                "given_name": "Riley",
                "family_name": "Sundahl",
                "claim_email": "family@example.com",
                "birth_date": "2009-04-12",
                "attestation": {
                  "floor": "high_school"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created person, its attestation, and its creating grant.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "given_name": "Riley",
                  "family_name": "Sundahl",
                  "middle_name": null,
                  "status": "active",
                  "claimed_at": null,
                  "claimed_by_relationship": null,
                  "links": [],
                  "created_at": "2026-08-01T12:00:00.000Z",
                  "updated_at": "2026-08-01T12:00:00.000Z",
                  "claim_email": "family@example.com",
                  "attestation": {
                    "floor": "high_school",
                    "attested_at": "2026-08-20T18:00:00.000Z"
                  },
                  "grant": {
                    "id": "99887766554433221100ffeeddccbbaa",
                    "state": "provisional",
                    "training_access": "write",
                    "checkins_access": "write",
                    "provisional_expires_at": "2027-02-16T18:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/people/{id}": {
      "get": {
        "operationId": "getPerson",
        "summary": "Get one person by id",
        "description": "Tier 1. ID-03: an id the caller holds no grant on is 404, indistinguishable from a nonexistent id. ENT-09: a retired person's row is served forever, links included.",
        "tags": [
          "people"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PersonId"
          }
        ],
        "responses": {
          "200": {
            "description": "The person.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Person"
                },
                "example": {
                  "id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "given_name": "Riley",
                  "family_name": "Sundahl",
                  "middle_name": null,
                  "status": "active",
                  "claimed_at": null,
                  "claimed_by_relationship": null,
                  "links": [],
                  "created_at": "2026-08-01T12:00:00.000Z",
                  "updated_at": "2026-08-01T12:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/people/{id}/dob-assertion": {
      "post": {
        "operationId": "assertDob",
        "summary": "Check a supplied birth date against the stored hash",
        "description": "Tier 3 (ID-07). Agree/disagree only; the platform never returns the birth date. Rate-limited 10/hour per (team, person), failing closed.",
        "tags": [
          "people"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PersonId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "birth_date"
                ],
                "additionalProperties": false,
                "properties": {
                  "birth_date": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "example": {
                "birth_date": "2009-04-12"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The assertion result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "result"
                  ],
                  "properties": {
                    "result": {
                      "type": "string",
                      "enum": [
                        "agree",
                        "disagree"
                      ],
                      "description": "Whether the supplied birth_date hashes to the stored value. Closed two-value response; no fallback, since a breaking addition would ship as /v2/ (PRIN-02)."
                    }
                  }
                },
                "example": {
                  "result": "agree"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/person-links": {
      "post": {
        "operationId": "proposeCandidateLink",
        "summary": "Propose a candidate same-person link",
        "description": "Tier 3 (ID-08). Never self-disposed: the athlete at claim time or an operator disposes it. Re-proposing a negative-disposed pair fails (ID-06).",
        "tags": [
          "person-links"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "person_id_a",
                  "person_id_b"
                ],
                "additionalProperties": false,
                "properties": {
                  "person_id_a": {
                    "type": "string",
                    "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
                  },
                  "person_id_b": {
                    "type": "string",
                    "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
                  }
                }
              },
              "example": {
                "person_id_a": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                "person_id_b": "2b3c4d5e6f708192a3b4c5d6e7f80912"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The proposed (or already-existing non-negative) candidate link.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "id": "4455667788990011223344556677889a",
                  "person_id_a": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "person_id_b": "2b3c4d5e6f708192a3b4c5d6e7f80912",
                  "disposition": "pending"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/training-sessions": {
      "get": {
        "operationId": "listTrainingSessions",
        "summary": "List training sessions",
        "description": "Tier 1. GR-04-windowed, MW-01-origin-scoped, MW-06 duplicate hints included.",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of reachable training sessions.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "LIM-02: a strong ETag over the page body."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_cursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TrainingSession"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "example": {
                  "items": [
                    {
                      "id": "aabbccdd11223344556677889900aabb",
                      "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "date": "2026-08-20",
                      "instant_utc": null,
                      "tz": null,
                      "technique": "classic",
                      "note": "Distance day, felt strong.",
                      "rpe": 4,
                      "distance_km": 12.5,
                      "duration_seconds": 3600,
                      "modality": [
                        {
                          "system": "xca",
                          "code": "skiing",
                          "display": "Skiing"
                        }
                      ],
                      "modality_text": null,
                      "blocks": [
                        {
                          "position": 0,
                          "reps": 1,
                          "duration_seconds": 3600,
                          "zone": "L2"
                        }
                      ],
                      "computed": {
                        "block_seconds": 3600,
                        "seconds_by_zone": {
                          "L2": 3600
                        }
                      },
                      "duplicates": [],
                      "external_ref": "ecxc-2026-08-20-riley",
                      "origin_team_id": "01020304050607080910111213141516",
                      "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "version": 1,
                      "created_at": "2026-08-20T18:00:00.000Z",
                      "updated_at": "2026-08-20T18:00:00.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "operationId": "createTrainingSession",
        "summary": "Record a training session",
        "description": "Tier 2 (WR-01..09). Accepts Idempotency-Key. A session write carrying blocks replaces the whole block set (WR-06).",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrainingSessionCreateRequest"
              },
              "example": {
                "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                "date": "2026-08-20",
                "modality": [
                  {
                    "system": "xca",
                    "code": "skiing",
                    "display": "Skiing"
                  }
                ],
                "technique": "classic",
                "duration_seconds": 3600,
                "blocks": [
                  {
                    "reps": 1,
                    "duration_seconds": 3600,
                    "zone": "L2"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created session, read back through GET (computed fields included).",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingSession"
                },
                "example": {
                  "id": "aabbccdd11223344556677889900aabb",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "instant_utc": null,
                  "tz": null,
                  "technique": "classic",
                  "note": "Distance day, felt strong.",
                  "rpe": 4,
                  "distance_km": 12.5,
                  "duration_seconds": 3600,
                  "modality": [
                    {
                      "system": "xca",
                      "code": "skiing",
                      "display": "Skiing"
                    }
                  ],
                  "modality_text": null,
                  "blocks": [
                    {
                      "position": 0,
                      "reps": 1,
                      "duration_seconds": 3600,
                      "zone": "L2"
                    }
                  ],
                  "computed": {
                    "block_seconds": 3600,
                    "seconds_by_zone": {
                      "L2": 3600
                    }
                  },
                  "duplicates": [],
                  "external_ref": "ecxc-2026-08-20-riley",
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T18:00:00.000Z",
                  "updated_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/ExternalRefConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/training-sessions/{id}": {
      "get": {
        "operationId": "getTrainingSession",
        "summary": "Get one training session by id",
        "description": "Tier 1. ID-03 masking; carries MW-02's weak ETag for If-Match.",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingSession"
                },
                "example": {
                  "id": "aabbccdd11223344556677889900aabb",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "instant_utc": null,
                  "tz": null,
                  "technique": "classic",
                  "note": "Distance day, felt strong.",
                  "rpe": 4,
                  "distance_km": 12.5,
                  "duration_seconds": 3600,
                  "modality": [
                    {
                      "system": "xca",
                      "code": "skiing",
                      "display": "Skiing"
                    }
                  ],
                  "modality_text": null,
                  "blocks": [
                    {
                      "position": 0,
                      "reps": 1,
                      "duration_seconds": 3600,
                      "zone": "L2"
                    }
                  ],
                  "computed": {
                    "block_seconds": 3600,
                    "seconds_by_zone": {
                      "L2": 3600
                    }
                  },
                  "duplicates": [],
                  "external_ref": "ecxc-2026-08-20-riley",
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T18:00:00.000Z",
                  "updated_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "patch": {
        "operationId": "updateTrainingSession",
        "summary": "Partially update a training session (JSON Merge Patch)",
        "description": "Tier 2 (WR-05, MW-02). Origin-scoped (MW-01): only the originating team may write. computed is rejected (ENT-05).",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          },
          {
            "$ref": "#/components/parameters/IfMatch"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "note": "Felt strong on the last interval."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated session, read back.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingSession"
                },
                "example": {
                  "id": "aabbccdd11223344556677889900aabb",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "instant_utc": null,
                  "tz": null,
                  "technique": "classic",
                  "note": "Distance day, felt strong.",
                  "rpe": 4,
                  "distance_km": 12.5,
                  "duration_seconds": 3600,
                  "modality": [
                    {
                      "system": "xca",
                      "code": "skiing",
                      "display": "Skiing"
                    }
                  ],
                  "modality_text": null,
                  "blocks": [
                    {
                      "position": 0,
                      "reps": 1,
                      "duration_seconds": 3600,
                      "zone": "L2"
                    }
                  ],
                  "computed": {
                    "block_seconds": 3600,
                    "seconds_by_zone": {
                      "L2": 3600
                    }
                  },
                  "duplicates": [],
                  "external_ref": "ecxc-2026-08-20-riley",
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T18:00:00.000Z",
                  "updated_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/ExternalRefConflict"
          },
          "412": {
            "$ref": "#/components/responses/PreconditionFailed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "428": {
            "$ref": "#/components/responses/PreconditionRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "operationId": "deleteTrainingSession",
        "summary": "Delete a training session",
        "description": "Tier 2 (MW-01, MW-02, WR-09). Produces a delete feed entry.",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SessionId"
          },
          {
            "$ref": "#/components/parameters/IfMatch"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "412": {
            "$ref": "#/components/responses/PreconditionFailed"
          },
          "428": {
            "$ref": "#/components/responses/PreconditionRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/training-sessions/by-ref/{ref}": {
      "put": {
        "operationId": "upsertTrainingSessionByRef",
        "summary": "Create-or-update a training session by external_ref",
        "description": "Tier 2 (WR-02..04). Team-namespaced, no global fallback; forwards across a person retirement. 201 on create, 200 on update.",
        "tags": [
          "training-sessions"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Ref"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrainingSessionCreateRequest"
              },
              "example": {
                "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                "date": "2026-08-20",
                "modality": "skiing"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated an existing row.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingSession"
                },
                "example": {
                  "id": "aabbccdd11223344556677889900aabb",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "instant_utc": null,
                  "tz": null,
                  "technique": "classic",
                  "note": "Distance day, felt strong.",
                  "rpe": 4,
                  "distance_km": 12.5,
                  "duration_seconds": 3600,
                  "modality": [
                    {
                      "system": "xca",
                      "code": "skiing",
                      "display": "Skiing"
                    }
                  ],
                  "modality_text": null,
                  "blocks": [
                    {
                      "position": 0,
                      "reps": 1,
                      "duration_seconds": 3600,
                      "zone": "L2"
                    }
                  ],
                  "computed": {
                    "block_seconds": 3600,
                    "seconds_by_zone": {
                      "L2": 3600
                    }
                  },
                  "duplicates": [],
                  "external_ref": "ecxc-2026-08-20-riley",
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T18:00:00.000Z",
                  "updated_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "201": {
            "description": "Created a new row.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingSession"
                },
                "example": {
                  "id": "aabbccdd11223344556677889900aabb",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "instant_utc": null,
                  "tz": null,
                  "technique": "classic",
                  "note": "Distance day, felt strong.",
                  "rpe": 4,
                  "distance_km": 12.5,
                  "duration_seconds": 3600,
                  "modality": [
                    {
                      "system": "xca",
                      "code": "skiing",
                      "display": "Skiing"
                    }
                  ],
                  "modality_text": null,
                  "blocks": [
                    {
                      "position": 0,
                      "reps": 1,
                      "duration_seconds": 3600,
                      "zone": "L2"
                    }
                  ],
                  "computed": {
                    "block_seconds": 3600,
                    "seconds_by_zone": {
                      "L2": 3600
                    }
                  },
                  "duplicates": [],
                  "external_ref": "ecxc-2026-08-20-riley",
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T18:00:00.000Z",
                  "updated_at": "2026-08-20T18:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/checkins": {
      "get": {
        "operationId": "listCheckins",
        "summary": "List check-ins",
        "description": "Tier 1. Requires the checkins grant scope (ENT-07), separately from training.",
        "tags": [
          "checkins"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of reachable check-ins.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "LIM-02: a strong ETag over the page body."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_cursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Checkin"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "example": {
                  "items": [
                    {
                      "id": "ccdd11223344aabb556677889900ccdd",
                      "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "date": "2026-08-20",
                      "feeling": "good",
                      "time_in_bed_hours": 8,
                      "note": null,
                      "external_ref": null,
                      "origin_team_id": "01020304050607080910111213141516",
                      "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "version": 1,
                      "created_at": "2026-08-20T07:00:00.000Z",
                      "updated_at": "2026-08-20T07:00:00.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "operationId": "createCheckin",
        "summary": "Record a daily check-in",
        "description": "Tier 2. Unique per (person, date) regardless of origin (MW-05); a second create for the same pair is checkin_conflict.",
        "tags": [
          "checkins"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinCreateRequest"
              },
              "example": {
                "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                "date": "2026-08-20",
                "feeling": "good",
                "time_in_bed_hours": 8
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created check-in.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "id": "ccdd11223344aabb556677889900ccdd",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "feeling": "good",
                  "time_in_bed_hours": 8,
                  "note": null,
                  "external_ref": null,
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T07:00:00.000Z",
                  "updated_at": "2026-08-20T07:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/CheckinConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/checkins/{id}": {
      "get": {
        "operationId": "getCheckin",
        "summary": "Get one check-in by id",
        "description": "Tier 1. ID-03 masking, scoped to the checkins grant.",
        "tags": [
          "checkins"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CheckinId"
          }
        ],
        "responses": {
          "200": {
            "description": "The check-in.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "id": "ccdd11223344aabb556677889900ccdd",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "feeling": "good",
                  "time_in_bed_hours": 8,
                  "note": null,
                  "external_ref": null,
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T07:00:00.000Z",
                  "updated_at": "2026-08-20T07:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "patch": {
        "operationId": "updateCheckin",
        "summary": "Partially update a check-in (JSON Merge Patch)",
        "description": "Tier 2 (WR-05, MW-02). Origin-scoped (MW-01).",
        "tags": [
          "checkins"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CheckinId"
          },
          {
            "$ref": "#/components/parameters/IfMatch"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "feeling": "great"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated check-in, read back.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "id": "ccdd11223344aabb556677889900ccdd",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "feeling": "good",
                  "time_in_bed_hours": 8,
                  "note": null,
                  "external_ref": null,
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T07:00:00.000Z",
                  "updated_at": "2026-08-20T07:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/CheckinConflict"
          },
          "412": {
            "$ref": "#/components/responses/PreconditionFailed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "428": {
            "$ref": "#/components/responses/PreconditionRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "operationId": "deleteCheckin",
        "summary": "Delete a check-in",
        "description": "Tier 2 (MW-01, MW-02, WR-09).",
        "tags": [
          "checkins"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CheckinId"
          },
          {
            "$ref": "#/components/parameters/IfMatch"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "412": {
            "$ref": "#/components/responses/PreconditionFailed"
          },
          "428": {
            "$ref": "#/components/responses/PreconditionRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/checkins/by-ref/{ref}": {
      "put": {
        "operationId": "upsertCheckinByRef",
        "summary": "Create-or-update a check-in by external_ref",
        "description": "Tier 2 (WR-02..04). Same natural-key upsert shape as training sessions.",
        "tags": [
          "checkins"
        ],
        "x-tier": 2,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Ref"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinCreateRequest"
              },
              "example": {
                "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                "date": "2026-08-20",
                "feeling": "good"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated an existing row.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "id": "ccdd11223344aabb556677889900ccdd",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "feeling": "good",
                  "time_in_bed_hours": 8,
                  "note": null,
                  "external_ref": null,
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T07:00:00.000Z",
                  "updated_at": "2026-08-20T07:00:00.000Z"
                }
              }
            }
          },
          "201": {
            "description": "Created a new row.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "id": "ccdd11223344aabb556677889900ccdd",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "date": "2026-08-20",
                  "feeling": "good",
                  "time_in_bed_hours": 8,
                  "note": null,
                  "external_ref": null,
                  "origin_team_id": "01020304050607080910111213141516",
                  "origin_person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "version": 1,
                  "created_at": "2026-08-20T07:00:00.000Z",
                  "updated_at": "2026-08-20T07:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/CheckinConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/grants": {
      "post": {
        "operationId": "inviteGrant",
        "summary": "Invite a pending grant on an existing person",
        "description": "Tier 3 (GR-01, GR-07 read defaults). The athlete accepts on the ownership surface, never through a consumer token.",
        "tags": [
          "grants"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GrantInviteRequest"
              },
              "example": {
                "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The invited grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Grant"
                },
                "example": {
                  "id": "99887766554433221100ffeeddccbbaa",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "state": "pending",
                  "training_access": "read",
                  "checkins_access": "read"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/grants/{id}": {
      "delete": {
        "operationId": "relinquishGrant",
        "summary": "Relinquish this team's own grant",
        "description": "Tier 3 (GR-02). Idempotent; narrows the team to origin-only read at once (GR-05).",
        "tags": [
          "grants"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GrantId"
          }
        ],
        "responses": {
          "204": {
            "description": "Relinquished."
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/join-codes/redeem": {
      "post": {
        "operationId": "redeemJoinCode",
        "summary": "Redeem an athlete-generated join code",
        "description": "Tier 3 (ID-02). The code travels in the JSON body, never the URL (bearer-material handling, RFC 6750 2.3). Yields a pending, read-default grant.",
        "tags": [
          "join-codes"
        ],
        "x-tier": 3,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GrantRedeemRequest"
              },
              "example": {
                "code": "ATHLETE-JOIN-CODE-1234"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The redeemed grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Grant"
                },
                "example": {
                  "id": "99887766554433221100ffeeddccbbaa",
                  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                  "state": "pending",
                  "training_access": "read",
                  "checkins_access": "read"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "403": {
            "$ref": "#/components/responses/TierInsufficient"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/GrantConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/rollups": {
      "get": {
        "operationId": "listRollups",
        "summary": "Per-(person, week, origin team) training totals",
        "description": "Tier 1. One call per coach table. Each session contributes its own stored duration_seconds when present, else (ENT-05) the sum of its blocks' duration_seconds times reps. dedupe=clustered subtracts MW-06 non-representative duplicate-cluster members before summing.",
        "tags": [
          "rollups"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many PEOPLE this page of the caller's granted person set covers, clamped to [1, 500]; default 100. Bounds the people summed into rollup rows, not the row count itself: one person can contribute several rows (one per week and origin team), so a page can return more than `limit` rows.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "description": "Inclusive range start (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "description": "Inclusive range end (YYYY-MM-DD). Must be within 366 days of from (a full training year); a wider span is validation_failed, not a silent truncation.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "week_start",
            "in": "query",
            "required": false,
            "description": "Which day starts a week.",
            "schema": {
              "type": "string",
              "enum": [
                "monday",
                "sunday"
              ],
              "default": "monday"
            }
          },
          {
            "name": "dedupe",
            "in": "query",
            "required": false,
            "description": "The only accepted value is clustered (MW-06); anything else is refused rather than silently ignored.",
            "schema": {
              "type": "string",
              "enum": [
                "clustered"
              ]
            }
          },
          {
            "name": "team",
            "in": "query",
            "required": false,
            "description": "Must equal the caller's own team id when sent (ID-03: no cross-team oracle).",
            "schema": {
              "type": "string",
              "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of rollup rows.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "LIM-02: a strong ETag over the page body."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_cursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RollupRow"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "example": {
                  "items": [
                    {
                      "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "week_start": "2026-08-17",
                      "week_end": "2026-08-23",
                      "origin_team_id": "01020304050607080910111213141516",
                      "duration_seconds": 14400
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/changes": {
      "get": {
        "operationId": "listChanges",
        "summary": "The ordered change feed",
        "description": "Tier 1 (SYNC-01..07). The conformant sync mechanism; poll every 5 minutes. A cursor older than the retention window returns resync_required.",
        "tags": [
          "changes"
        ],
        "x-tier": 1,
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the change feed.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "LIM-02: a strong ETag over the page body."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_cursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChangeEntry"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "example": {
                  "items": [
                    {
                      "seq": 42,
                      "op": "upsert",
                      "entity": "training_session",
                      "entity_id": "aabbccdd11223344556677889900aabb",
                      "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
                      "origin_consumer_id": "01020304050607080910111213141516",
                      "external_ref": "ecxc-2026-08-20-riley",
                      "occurred_at": "2026-08-20T18:00:00.000Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/TokenInvalid"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "410": {
            "$ref": "#/components/responses/ResyncRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "xca_live_/xca_test_ token (AUTH-01)",
        "description": "PRIN-01: tokens travel only in the Authorization header, never in a URL."
      }
    },
    "parameters": {
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "An opaque pagination cursor from a previous page's next_cursor.",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size, clamped to [1, 500]; default 100.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 500,
          "default": 100
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "required": false,
        "description": "SYNC-06 convenience filter, at-least-once; GET /v1/changes is the conformant sync mechanism (SYNC-01).",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "IfMatch": {
        "name": "If-Match",
        "in": "header",
        "required": true,
        "description": "MW-02: the row's current version, quoted (W/\"{version}\"). Missing fails 428; stale fails 412.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "WR-01: a replay of the same key and body returns the stored response; the same key with a different body is a conflict.",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "PersonId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
        }
      },
      "SessionId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
        }
      },
      "CheckinId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
        }
      },
      "GrantId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
        }
      },
      "Ref": {
        "name": "ref",
        "in": "path",
        "required": true,
        "description": "WR-02: the caller's own external_ref, team-namespaced.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "TokenInvalid": {
        "description": "The bearer token is missing, malformed, or does not resolve (token_invalid), or has been revoked (token_revoked).",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/token_invalid",
              "title": "Token invalid",
              "status": 401,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "The Authorization header did not carry a resolvable token."
            }
          }
        }
      },
      "TierInsufficient": {
        "description": "The token's tier does not admit this operation (AUTH-03).",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/tier_insufficient",
              "title": "Tier insufficient",
              "status": 403,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "This operation requires a higher tier than this token carries."
            }
          }
        }
      },
      "ScopeMissing": {
        "description": "The token holds no live grant at write access for this row's scope (WR-08, GR-07).",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/scope_missing",
              "title": "Scope missing",
              "status": 403,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "This token holds no write access for this scope."
            }
          }
        }
      },
      "NotFound": {
        "description": "ID-03: the object does not exist, or the caller holds no grant reaching it; both cases are byte-identical.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/not_found",
              "title": "Not found",
              "status": 404,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "No reachable row for this id."
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "This route does not support the request method.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          },
          "Allow": {
            "schema": {
              "type": "string"
            },
            "description": "RFC 9110 10.2.1: the methods this route does support."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/method_not_allowed",
              "title": "Method not allowed",
              "status": 405,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "This route does not support this method."
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "WR-01: this Idempotency-Key was already used for a different request body, or the original request is still in flight.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/idempotency_conflict",
              "title": "Idempotency key conflict",
              "status": 409,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "This Idempotency-Key was already used for a different request."
            }
          }
        }
      },
      "ExternalRefConflict": {
        "description": "WR-02: external_ref already names another of this team's rows.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/external_ref_conflict",
              "title": "External reference conflict",
              "status": 409,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "external_ref already names another row."
            }
          }
        }
      },
      "CheckinConflict": {
        "description": "MW-05/ENT-07: a check-in already exists for this person on this date.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/checkin_conflict",
              "title": "Check-in conflict",
              "status": 409,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "A check-in already exists for this person on this date."
            }
          }
        }
      },
      "GrantConflict": {
        "description": "A concurrent grant transition landed between the read and the write; re-read the grant and retry.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/grant_conflict",
              "title": "Grant conflict",
              "status": 409,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "A concurrent grant change landed on this pair; re-read and retry."
            }
          }
        }
      },
      "ResyncRequired": {
        "description": "SYNC-05: cursor names a point older than the retention window; start a full resync.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/resync_required",
              "title": "Resync required",
              "status": 410,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "This cursor is older than the change-feed retention window."
            }
          }
        }
      },
      "PreconditionFailed": {
        "description": "MW-02: the If-Match precondition did not match the row's current version.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/precondition_failed",
              "title": "Precondition failed",
              "status": 412,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "If-Match did not match the row's current version."
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "The request body failed validation; errors[] names each failing field.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/validation_failed",
              "title": "Validation failed",
              "status": 422,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "One or more fields failed validation."
            }
          }
        }
      },
      "ComputedFieldReadonly": {
        "description": "ENT-05: a computed field was named on a write.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/computed_field_readonly",
              "title": "Computed field is read-only",
              "status": 422,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "\"computed\" is a read-only computed field."
            }
          }
        }
      },
      "DateInFuture": {
        "description": "WR-07: a training row was dated more than one calendar day in the future.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/date_in_future",
              "title": "Date in future",
              "status": 422,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "\"date\" may not be more than one calendar day in the future."
            }
          }
        }
      },
      "PreconditionRequired": {
        "description": "MW-02: If-Match is required for this operation and was not sent.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/precondition_required",
              "title": "Precondition required",
              "status": 428,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "If-Match is required for this operation."
            }
          }
        }
      },
      "RateLimited": {
        "description": "LIM-01/LIM-03: the caller has exceeded a published rate limit.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until the window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/rate_limited",
              "title": "Rate limited",
              "status": 429,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "Rate limit exceeded."
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "ERR-03: a deployment fault (missing binding or secret, or a read-back invariant violation).",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: this token's request budget for the window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "LIM-01: seconds until the current window resets."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://xcathletes.org/docs/errors/service_unavailable",
              "title": "Service unavailable",
              "status": 503,
              "request_id": "3f9c2a1b7e4d4f5a9c6b8e2d1a4f7c3e",
              "detail": "A deployment fault prevented this request from completing."
            }
          }
        }
      }
    },
    "schemas": {
      "Person": {
        "title": "person",
        "description": "GET /v1/people, GET /v1/people/{id}: the whole athlete entity (ENT-04, ENT-09). Response schema, PRIN-04 open: additionalProperties permitted, since /v1/ only ever adds optional fields within v1 (PRIN-02).",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "id",
          "given_name",
          "family_name",
          "middle_name",
          "status",
          "claimed_at",
          "claimed_by_relationship",
          "links",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "PRIN-05: 128-bit random hex id, opaque, never reused. A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "given_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "family_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "retired",
              null
            ],
            "description": "ENT-09. An unrecognized status falls back to `active`: treat the record as normal unless links[] carries a replaced_by forwarding entry."
          },
          "claimed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "OWN-01: null until the athlete claims the record."
          },
          "claimed_by_relationship": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "self",
              "guardian",
              null
            ],
            "description": "OWN-08. An unrecognized value falls back to `guardian` (the more restrictive assumption)."
          },
          "links": {
            "type": "array",
            "description": "ENT-09: alias edges, `replaced_by` today; a person a caller reaches through a retired id resolves forward through this list, forever.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "required": [
                "relation",
                "linked_person_id"
              ],
              "properties": {
                "relation": {
                  "type": "string",
                  "description": "Uncatalogued by design (room for a second kind beyond replaced_by); an unrecognized relation falls back to being ignored by a conforming consumer (PRIN-03)."
                },
                "linked_person_id": {
                  "type": "string",
                  "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PersonCreateRequest": {
        "title": "person create request",
        "description": "POST /v1/people (ID-01, Tier 3). Request schema, PRIN-04 strict: additionalProperties forbidden.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "given_name",
          "family_name",
          "claim_email",
          "birth_date",
          "attestation"
        ],
        "properties": {
          "given_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "family_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "middle_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "claim_email": {
            "type": "string",
            "format": "email",
            "description": "OWN-01/05: the proposed claim address; unvalidated until the athlete claims."
          },
          "birth_date": {
            "type": "string",
            "format": "date",
            "description": "ENT-04: hashed under a per-person salt and discarded immediately; the platform never stores this value."
          },
          "attestation": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "floor"
            ],
            "properties": {
              "floor": {
                "type": "string",
                "enum": [
                  "high_school",
                  "age_14_plus"
                ],
                "description": "FLOOR-01. No fallback: an unrecognized value fails validation_failed rather than being silently accepted, since this is the platform's one age-floor gate."
              }
            }
          }
        }
      },
      "TrainingSession": {
        "title": "training_session",
        "description": "GET /v1/training-sessions, GET /v1/training-sessions/{id}: one recorded session. Response schema, PRIN-04 open.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "id",
          "person_id",
          "date",
          "instant_utc",
          "tz",
          "technique",
          "note",
          "rpe",
          "distance_km",
          "duration_seconds",
          "modality",
          "modality_text",
          "blocks",
          "computed",
          "duplicates",
          "external_ref",
          "origin_team_id",
          "origin_person_id",
          "version",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity 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 team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "ENT-01: the athlete's local calendar date, no time part."
          },
          "instant_utc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "tz": {
            "type": [
              "string",
              "null"
            ],
            "description": "An IANA zone name, or null."
          },
          "technique": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "classic",
              "skate",
              "double_pole",
              null
            ],
            "description": "Required on skiing/roller_skiing, null on every other modality. An unrecognized value falls back to null (treat as unspecified technique)."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "rpe": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 10
          },
          "distance_km": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "ENT-02/ENT-05: when present, authoritative over the block sum."
          },
          "modality": {
            "type": "array",
            "description": "ENT-06: one or more codings; the platform's own registry versions independently of /v1/.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "required": [
                "system",
                "code",
                "display"
              ],
              "properties": {
                "system": {
                  "type": "string"
                },
                "code": {
                  "type": "string",
                  "description": "PRIN-03: a code this consumer's own registry does not recognize falls back to `other`."
                },
                "display": {
                  "type": "string"
                }
              }
            }
          },
          "modality_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TrainingBlock"
            }
          },
          "computed": {
            "type": "object",
            "description": "ENT-05: read-only, published formula (block_seconds = sum(duration_seconds * reps); seconds_by_zone groups the same sum by zone). Rejected on write (computed_field_readonly).",
            "additionalProperties": true,
            "required": [
              "block_seconds",
              "seconds_by_zone"
            ],
            "properties": {
              "block_seconds": {
                "type": "integer",
                "minimum": 0
              },
              "seconds_by_zone": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "duplicates": {
            "type": "array",
            "description": "MW-06: direct pairwise duplicate-cluster hints, confidence-marked; never causes a row to be omitted.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "required": [
                "session_id",
                "confidence"
              ],
              "properties": {
                "session_id": {
                  "type": "string",
                  "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
                },
                "confidence": {
                  "type": "string",
                  "enum": [
                    "high",
                    "medium"
                  ],
                  "description": "MW-06. An unrecognized value falls back to `medium` (the more cautious hint)."
                }
              }
            }
          },
          "external_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "WR-02: the originating consumer's own natural key."
          },
          "origin_team_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ENT-08. The originating team id, never a token id (AUTH-04). A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "origin_person_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "version": {
            "type": "integer",
            "minimum": 1,
            "description": "MW-02: the optimistic-locking version an If-Match precondition names."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TrainingSessionCreateRequest": {
        "title": "training_session create request",
        "description": "POST /v1/training-sessions (WR-01..09, TIER-02). Request schema, PRIN-04 strict: additionalProperties forbidden, computed rejected outright (ENT-05).",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "person_id",
          "date",
          "modality"
        ],
        "properties": {
          "person_id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "instant_utc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "tz": {
            "type": [
              "string",
              "null"
            ]
          },
          "technique": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "classic",
              "skate",
              "double_pole",
              null
            ],
            "description": "Required on skiing/roller_skiing, null on every other modality. An unrecognized value falls back to null (treat as unspecified technique)."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "rpe": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 10
          },
          "distance_km": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 500
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 86400
          },
          "modality_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "external_ref": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "modality": {
            "description": "ENT-06's coding array, or a legacy bare modality string (backward-compatible wire shape).",
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "running",
                  "hill_bounding",
                  "strength",
                  "spenst",
                  "skiing",
                  "roller_skiing",
                  "bike",
                  "other"
                ],
                "description": "The legacy bare modality string. PRIN-03: a code this consumer's own registry does not recognize falls back to `other`."
              },
              {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "system",
                    "code",
                    "display"
                  ],
                  "properties": {
                    "system": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200
                    },
                    "code": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200
                    },
                    "display": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200
                    }
                  }
                }
              }
            ]
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TrainingBlockWriteItem"
            }
          }
        }
      },
      "TrainingBlock": {
        "title": "training_block",
        "description": "One entry of training_session.blocks[]. Not independently addressable: a session write carrying `blocks` replaces the whole set (WR-06). Response schema, PRIN-04 open.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "position",
          "reps",
          "duration_seconds",
          "zone"
        ],
        "properties": {
          "position": {
            "type": "integer",
            "minimum": 0,
            "description": "Wire order within the session's blocks array."
          },
          "reps": {
            "type": "integer",
            "minimum": 1
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "ENT-02: integer seconds. null on a legacy row this migration has not backfilled, which the ENT-05 computed formula treats as zero."
          },
          "zone": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "L1",
              "L2",
              "L3",
              "L4",
              "L5",
              null
            ],
            "description": "ENT-03: the lactate-turn-point zone; null for strength and spenst blocks. An unrecognized value falls back to null (treat as unzoned)."
          }
        }
      },
      "TrainingBlockWriteItem": {
        "title": "training_block write item",
        "description": "One entry of a training-session create/update request's `blocks` array (WR-06). Request schema, PRIN-04 strict.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reps",
          "duration_seconds",
          "zone"
        ],
        "properties": {
          "reps": {
            "type": "integer",
            "minimum": 1
          },
          "duration_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 86400
          },
          "zone": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "L1",
              "L2",
              "L3",
              "L4",
              "L5",
              null
            ],
            "description": "ENT-03: the lactate-turn-point zone; null for strength and spenst blocks. An unrecognized value falls back to null (treat as unzoned)."
          }
        }
      },
      "Checkin": {
        "title": "checkin",
        "description": "GET /v1/checkins, GET /v1/checkins/{id}: one daily check-in, unique per person per local calendar date (ENT-07). Response schema, PRIN-04 open.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "id",
          "person_id",
          "date",
          "feeling",
          "time_in_bed_hours",
          "note",
          "external_ref",
          "origin_team_id",
          "origin_person_id",
          "version",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity 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 team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "feeling": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "poor",
              "ok",
              "good",
              "great",
              null
            ],
            "description": "The one-tap feeling scale. An unrecognized value falls back to null (treat as unreported)."
          },
          "time_in_bed_hours": {
            "type": [
              "number",
              "null"
            ],
            "enum": [
              5.5,
              6,
              6.5,
              7,
              7.5,
              8,
              8.5,
              9,
              9.5,
              10,
              null
            ],
            "description": "Half-hour picks. An unrecognized value falls back to null (treat as unreported)."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "external_ref": {
            "type": [
              "string",
              "null"
            ]
          },
          "origin_team_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "origin_person_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckinCreateRequest": {
        "title": "checkin create request",
        "description": "POST /v1/checkins (ENT-07, requires the checkins grant scope). Request schema, PRIN-04 strict: additionalProperties forbidden; at least one of feeling, time_in_bed_hours, or note is required.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "person_id",
          "date"
        ],
        "anyOf": [
          {
            "required": [
              "feeling"
            ]
          },
          {
            "required": [
              "time_in_bed_hours"
            ]
          },
          {
            "required": [
              "note"
            ]
          }
        ],
        "properties": {
          "person_id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "feeling": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "poor",
              "ok",
              "good",
              "great",
              null
            ],
            "description": "The one-tap feeling scale. An unrecognized value falls back to null (treat as unreported)."
          },
          "time_in_bed_hours": {
            "type": [
              "number",
              "null"
            ],
            "enum": [
              5.5,
              6,
              6.5,
              7,
              7.5,
              8,
              8.5,
              9,
              9.5,
              10,
              null
            ],
            "description": "Half-hour picks. An unrecognized value falls back to null (treat as unreported)."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "external_ref": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          }
        }
      },
      "Grant": {
        "title": "grant",
        "description": "POST /v1/grants, POST /v1/join-codes/redeem, DELETE /v1/grants/{id}: the team-to-person authorization link (GR-01..07). Response schema, PRIN-04 open.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "id",
          "person_id",
          "state",
          "training_access",
          "checkins_access"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          },
          "person_id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "state": {
            "type": "string",
            "enum": [
              "provisional",
              "pending",
              "accepted",
              "revoked"
            ],
            "description": "GR-01..03. An unrecognized value falls back to `revoked` (treat the grant as ended; the more restrictive assumption)."
          },
          "training_access": {
            "type": "string",
            "enum": [
              "none",
              "read",
              "write"
            ],
            "description": "GR-07. An unrecognized value falls back to `none`."
          },
          "checkins_access": {
            "type": "string",
            "enum": [
              "none",
              "read",
              "write"
            ],
            "description": "GR-07: separately grantable from training_access (ENT-07). An unrecognized value falls back to `none`."
          },
          "log_visible_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "GR-04's visibility-window clamp, when a future read serves it."
          },
          "history_ack_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "GrantInviteRequest": {
        "title": "grant invite request",
        "description": "POST /v1/grants (GR-01, Tier 3). Request schema, PRIN-04 strict.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "person_id"
        ],
        "properties": {
          "person_id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          }
        }
      },
      "GrantRedeemRequest": {
        "title": "join code redeem request",
        "description": "POST /v1/join-codes/redeem (ID-02, Tier 3): the code travels in the JSON body, never the URL. Request schema, PRIN-04 strict.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "minLength": 1
          }
        }
      },
      "ChangeEntry": {
        "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 team, person, or entity 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 team, person, or entity 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). A pre-existing platform id (a legacy team, person, or entity 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 team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          }
        }
      },
      "RollupRow": {
        "title": "rollup_row",
        "description": "GET /v1/rollups?team&from&to&week_start: one (person, week, origin team) total. Response schema, PRIN-04 open. No enum-bearing fields.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "person_id",
          "week_start",
          "week_end",
          "origin_team_id",
          "duration_seconds"
        ],
        "properties": {
          "person_id": {
            "type": "string",
            "description": "A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "week_start": {
            "type": "string",
            "format": "date"
          },
          "week_end": {
            "type": "string",
            "format": "date"
          },
          "origin_team_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "null only for a legacy row minted before origin stamping shipped. A pre-existing platform id (a legacy team, person, or entity predating the /v1/ contract) or a new PRIN-05 128-bit hex id may both appear here; format is not otherwise constrained."
          },
          "duration_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "ENT-02: the integer-seconds sum for this person's sessions in this week, originated by this team. Each session contributes its own stored duration_seconds when present, else (ENT-05) the sum of its blocks' duration_seconds times reps. dedupe=clustered subtracts MW-06 non-representative cluster members before summing."
          }
        }
      },
      "Problem": {
        "title": "problem",
        "description": "Every /v1/ error response (ERR-01, RFC 9457 problem+json). Response schema, PRIN-04 open: the catalogue (ERR-02) grows by addition only (PRIN-02), so a type this consumer's own copy of the catalogue does not recognize falls back to treating the response as a generic error keyed on `status` alone.",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "type",
          "title",
          "status",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Resolves to the public error catalogue (ERR-02); an absolute origin, never the relative /docs/errors/{code} form. An unrecognized type falls back to the generic-error handling this schema's own description names."
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "request_id": {
            "type": "string",
            "description": "Echoed as the Request-Id response header; correlates a response with a server-side log line."
          },
          "detail": {
            "type": "string",
            "description": "Developer-audience explanation (ERR-03); never athlete- or coach-audience wording."
          },
          "errors": {
            "type": "array",
            "description": "ERR-01's multi-field validation extension.",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "required": [
                "field",
                "detail"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
