{"openapi":"3.1.0","info":{"title":"Rally Leagues API","version":"1.0.0","description":"Programmatic API for Rally leagues. Authentication is by ORGANIZATION KEY (`Authorization: Bearer rk_live_…`) and by it alone: a session cookie opens nothing here, and the organization is never named in a request — it is derived from the key. A key is not a person: it has no governance, no delegation and no implicit personal access. Managing keys and webhooks belongs to the signed-in session, never to a key. Every response carries the `X-Request-Id` header. This document exists in English (`/v1/openapi.json`) and in French (`/v1/openapi.fr.json`); error messages returned by the API are in French.","contact":{"name":"Rally","url":"https://gorally.games","email":"hello@gorally.games"}},"servers":[{"url":"https://gorally.games/v1"}],"security":[{"cleDOrganisation":[]}],"tags":[{"name":"Competition","description":"Leagues, seasons, categories, standings."},{"name":"Results","description":"Matches and result entry."},{"name":"Rosters","description":"Under `members:*` scopes only — the only surface that carries people's names."},{"name":"Events","description":"The same feed as webhooks, readable and replayable."}],"paths":{"/openapi.json":{"get":{"operationId":"lireContrat","tags":["Competition"],"security":[],"summary":"This document, in English.","description":"No key required: the contract can be read before integrating.","responses":{"200":{"description":"The OpenAPI document, in English.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object"},"example":{"openapi":"3.1.0","info":{"title":"Rally Leagues API","version":"1.0.0"}}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.fr.json":{"get":{"operationId":"lireContratFrancais","tags":["Competition"],"security":[],"summary":"This document, in French.","description":"No key required, like the English version.","responses":{"200":{"description":"The OpenAPI document, in French.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object"},"example":{"openapi":"3.1.0","info":{"title":"Rally — API des ligues","version":"1.0.0"}}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leagues":{"get":{"operationId":"listerLigues","tags":["Competition"],"summary":"The organization's leagues.","description":"Scope `read`. Cursor pagination on an immutable key: an existing row is seen exactly once, even under concurrent updates.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"example":25,"description":"Page size. Above 100, the request is refused rather than silently truncated."},{"name":"after","in":"query","required":false,"schema":{"type":"string"},"example":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","description":"Cursor: the identifier of the LAST item of the previous page. An unknown cursor is refused — it is not recovered. The three paginated lists — events, leagues, matches — follow the SAME rule: traversal is ordered on an internal insertion number that no update moves, and a page stops before any write whose transaction may still be in flight. A row written by a long transaction is therefore DELAYED, never skipped: it appears in its place as soon as its transaction commits, and exactly once. Renaming a league or correcting a match between two pages neither hides it nor brings it back."}],"responses":{"200":{"description":"A page of leagues.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/League"}},"has_more":{"type":"boolean"}}},"example":{"object":"list","data":[{"id":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","name":"Ligue du mardi","sport":"CURLING","sportLabel":null,"profile":{"key":"CURLING","label":"Curling","version":3,"resourceKind":"SHEET","cadence":{"unitsPerMatch":8,"minutesPerUnit":15,"matchesPerNight":1,"scoreTarget":null,"seasonWeeks":{"min":10,"max":30}},"vocabulary":{"resource":{"one":"feuille","many":"feuilles","genre":"F"},"unit":{"one":"bout","many":"bouts","genre":"M"},"match":{"one":"partie","many":"parties","genre":"F"},"scoreUnit":{"one":"point","many":"points","genre":"M"}}},"timeZone":"America/Toronto"}],"has_more":false}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"creerLigue","tags":["Competition"],"summary":"Creates a league.","description":"Scope `write`. The organization's commercial entitlement is checked as for any write.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"example":"6a1f0c3e-creer-ligue-mardi","description":"Replays the original response for 24 hours: a success, or a refusal returned during execution. A body rejected before that (malformed JSON, invalid field) does not reserve the key. Same key with a different body: 409 `IDEMPOTENCY_KEY_REUSED`. An execution still in flight answers 409 `IDEMPOTENT_REQUEST_IN_FLIGHT`. A 5xx is never stored: it means \"retry\". And if an execution is interrupted with an unknown outcome — a connection lost right after the commit returns the same error as one lost right before — the replay answers 409 `IDEMPOTENCY_OUTCOME_UNKNOWN` rather than risk a duplicate: read the resource again, then resume with a new key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLeague"},"example":{"name":"Ligue du mardi","sport":"CURLING","timeZone":"America/Toronto"}}}},"responses":{"201":{"description":"The created league.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/League"},"example":{"id":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","name":"Ligue du mardi","sport":"CURLING","sportLabel":null,"profile":{"key":"CURLING","label":"Curling","version":3,"resourceKind":"SHEET","cadence":{"unitsPerMatch":8,"minutesPerUnit":15,"matchesPerNight":1,"scoreTarget":null,"seasonWeeks":{"min":10,"max":30}},"vocabulary":{"resource":{"one":"feuille","many":"feuilles","genre":"F"},"unit":{"one":"bout","many":"bouts","genre":"M"},"match":{"one":"partie","many":"parties","genre":"F"},"scoreUnit":{"one":"point","many":"points","genre":"M"}}},"timeZone":"America/Toronto"}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key replayed with a different body.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over 16 KiB (`BODY_TOO_LARGE`). The public API carries no files.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leagues/{leagueId}":{"get":{"operationId":"lireLigue","tags":["Competition"],"summary":"A league and its seasons.","description":"Scope `read`.","parameters":[{"name":"leagueId","in":"path","required":true,"schema":{"type":"string"},"example":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f"}],"responses":{"200":{"description":"The league and its seasons.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeagueDetail"},"example":{"object":"league","id":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","name":"Ligue du mardi","sport":"CURLING","sportLabel":null,"profile":{"key":"CURLING","label":"Curling","version":3,"resourceKind":"SHEET","cadence":{"unitsPerMatch":8,"minutesPerUnit":15,"matchesPerNight":1,"scoreTarget":null,"seasonWeeks":{"min":10,"max":30}},"vocabulary":{"resource":{"one":"feuille","many":"feuilles","genre":"F"},"unit":{"one":"bout","many":"bouts","genre":"M"},"match":{"one":"partie","many":"parties","genre":"F"},"scoreUnit":{"one":"point","many":"points","genre":"M"}}},"timeZone":"America/Toronto","cadences":[{"id":"7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f","key":"HIVER","label":"Hiver","position":1}],"seasons":[{"id":"c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e","name":"Hiver 2027","cadenceId":"7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f","ordinal":1,"status":"SETUP","sportProfile":"CURLING","sportProfileVersion":3,"resourceKind":"SHEET","cadence":{"unitsPerMatch":8,"minutesPerUnit":15,"matchesPerNight":1,"scoreTarget":null,"seasonWeeks":{"min":10,"max":30}},"vocabulary":{"resource":{"one":"feuille","many":"feuilles","genre":"F"},"unit":{"one":"bout","many":"bouts","genre":"M"},"match":{"one":"partie","many":"parties","genre":"F"},"scoreUnit":{"one":"point","many":"points","genre":"M"}}}]}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leagues/{leagueId}/seasons":{"post":{"operationId":"creerSaison","tags":["Competition"],"summary":"Creates a season in a league.","description":"Scope `write`.","parameters":[{"name":"leagueId","in":"path","required":true,"schema":{"type":"string"},"example":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f"},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"example":"6a1f0c3e-creer-ligue-mardi","description":"Replays the original response for 24 hours: a success, or a refusal returned during execution. A body rejected before that (malformed JSON, invalid field) does not reserve the key. Same key with a different body: 409 `IDEMPOTENCY_KEY_REUSED`. An execution still in flight answers 409 `IDEMPOTENT_REQUEST_IN_FLIGHT`. A 5xx is never stored: it means \"retry\". And if an execution is interrupted with an unknown outcome — a connection lost right after the commit returns the same error as one lost right before — the replay answers 409 `IDEMPOTENCY_OUTCOME_UNKNOWN` rather than risk a duplicate: read the resource again, then resume with a new key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSeason"},"example":{"name":"Hiver 2027","cadenceId":"7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f","startsAt":"2027-01-05T00:00:00.000Z","endsAt":"2027-04-27T00:00:00.000Z"}}}},"responses":{"201":{"description":"The created season.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Season"},"example":{"id":"c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e","name":"Hiver 2027","cadenceId":"7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f","ordinal":1,"status":"SETUP","sportProfile":"CURLING","sportProfileVersion":3,"resourceKind":"SHEET","cadence":{"unitsPerMatch":8,"minutesPerUnit":15,"matchesPerNight":1,"scoreTarget":null,"seasonWeeks":{"min":10,"max":30}},"vocabulary":{"resource":{"one":"feuille","many":"feuilles","genre":"F"},"unit":{"one":"bout","many":"bouts","genre":"M"},"match":{"one":"partie","many":"parties","genre":"F"},"scoreUnit":{"one":"point","many":"points","genre":"M"}}}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key replayed with a different body.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over 16 KiB (`BODY_TOO_LARGE`). The public API carries no files.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/seasons/{seasonId}/categories":{"get":{"operationId":"listerCategories","tags":["Competition"],"summary":"The categories of a season.","description":"Scope `read`.","parameters":[{"name":"seasonId","in":"path","required":true,"schema":{"type":"string"},"example":"c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e"}],"responses":{"200":{"description":"The season's categories, returned in full.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}},"example":{"object":"list","data":[{"id":"e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f","seasonId":"c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e","name":"Mardi soir","phase":"REGULAR","revision":6,"policyRevision":1,"matchCount":28,"unplayedMatchCount":20,"unplayedRegularMatchCount":20,"registrationCapacity":8}]}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories/{categoryId}/standings":{"get":{"operationId":"lireClassement","tags":["Competition"],"summary":"The standings of a category.","description":"Scope `read`. Standings are COMPUTED on read and never stored: that is why there is no `standings.updated` webhook. A consumer who wants to follow them listens to `match.completed` and reads this again.","parameters":[{"name":"categoryId","in":"path","required":true,"schema":{"type":"string"},"example":"e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f"}],"responses":{"200":{"description":"The standings, ordered and returned in full.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/StandingRow"}}}},"example":{"object":"list","data":[{"leagueTeamId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","name":"Les Pierres","rank":1,"tied":false,"played":4,"wins":3,"draws":0,"losses":1,"scoreFor":29,"scoreAgainst":18,"diff":11,"gamePointsFor":0,"gamePointsAgainst":0,"points":9},{"leagueTeamId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","name":"Les Balais","rank":2,"tied":false,"played":4,"wins":2,"draws":1,"losses":1,"scoreFor":24,"scoreAgainst":21,"diff":3,"gamePointsFor":0,"gamePointsAgainst":0,"points":7}]}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/matches":{"get":{"operationId":"listerRencontres","tags":["Results"],"summary":"The organization's matches.","description":"Scope `read`. `revision` is the optimistic lock to send when writing a result.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"example":25,"description":"Page size. Above 100, the request is refused rather than silently truncated."},{"name":"after","in":"query","required":false,"schema":{"type":"string"},"example":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","description":"Cursor: the identifier of the LAST item of the previous page. An unknown cursor is refused — it is not recovered. The three paginated lists — events, leagues, matches — follow the SAME rule: traversal is ordered on an internal insertion number that no update moves, and a page stops before any write whose transaction may still be in flight. A row written by a long transaction is therefore DELAYED, never skipped: it appears in its place as soon as its transaction commits, and exactly once. Renaming a league or correcting a match between two pages neither hides it nor brings it back."}],"responses":{"200":{"description":"A page of matches.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Match"}},"has_more":{"type":"boolean"}}},"example":{"object":"list","data":[{"id":"f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c","categoryId":"e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f","status":"SCHEDULED","teamAId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamBId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","scoreA":null,"scoreB":null,"sets":null,"startsAt":"2027-01-13T00:15:00.000Z","revision":3}],"has_more":true}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/matches/{matchId}":{"patch":{"operationId":"ecrireResultat","tags":["Results"],"summary":"Writes or corrects the result of a match.","description":"Scope `write`. `expectedRevision` is REQUIRED: this compare-and-swap is what stops two programs from silently overwriting each other's result. A stale revision returns 409 — read the match again, do not retry blindly. No `Idempotency-Key` here: the revision lock already does that job, and two mechanisms would contradict each other.","parameters":[{"name":"matchId","in":"path","required":true,"schema":{"type":"string"},"example":"f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchResult"},"example":{"expectedRevision":3,"event":"COMPLETER","score":{"a":7,"b":5,"unitsPlayed":8}}}}},"responses":{"200":{"description":"The match after the write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object"},"example":{"id":"f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c","status":"COMPLETED","scoreA":7,"scoreB":5,"resultSide":"A","sets":null,"revision":4}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Stale revision, or a state that does not accept this command.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over 16 KiB (`BODY_TOO_LARGE`). The public API carries no files.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/{registrationId}/roster":{"get":{"operationId":"lireEffectif","tags":["Rosters"],"summary":"The roster of a registration.","description":"Scope `members:read`. This is the ONLY family of routes that returns people's labels, and an organization can deny it to a key without taking away the rest. No other part of the API — webhooks included — copies them.","parameters":[{"name":"registrationId","in":"path","required":true,"schema":{"type":"string"},"example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}],"responses":{"200":{"description":"The roster.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Roster"},"example":{"registrationId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Les Pierres","memberships":[{"personId":"3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a","label":{"kind":"NAME","value":"Camille Roy"},"role":"CAPTAIN","jerseyNumber":7,"endedAt":null}]}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"ajouterAuEffectif","tags":["Rosters"],"summary":"Adds a person to a roster.","description":"Scope `members:write`.","parameters":[{"name":"registrationId","in":"path","required":true,"schema":{"type":"string"},"example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"example":"6a1f0c3e-creer-ligue-mardi","description":"Replays the original response for 24 hours: a success, or a refusal returned during execution. A body rejected before that (malformed JSON, invalid field) does not reserve the key. Same key with a different body: 409 `IDEMPOTENCY_KEY_REUSED`. An execution still in flight answers 409 `IDEMPOTENT_REQUEST_IN_FLIGHT`. A 5xx is never stored: it means \"retry\". And if an execution is interrupted with an unknown outcome — a connection lost right after the commit returns the same error as one lost right before — the replay answers 409 `IDEMPOTENCY_OUTCOME_UNKNOWN` rather than risk a duplicate: read the resource again, then resume with a new key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMembership"},"example":{"personId":"3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a","role":"CAPTAIN","jerseyNumber":7}}}},"responses":{"200":{"description":"The roster after the addition.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Roster"},"example":{"registrationId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Les Pierres","memberships":[{"personId":"3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a","label":{"kind":"NAME","value":"Camille Roy"},"role":"CAPTAIN","jerseyNumber":7,"endedAt":null}]}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key replayed with a different body, or a state conflict.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over 16 KiB (`BODY_TOO_LARGE`). The public API carries no files.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events":{"get":{"operationId":"listerEvenements","tags":["Events"],"summary":"Structural events, oldest first.","description":"Scope `read`. CATCH-UP: this is what prevents \"we lost two days of webhooks\". The feed is exactly the webhook feed — same allowlist, same thin payload, same identifiers (`webhook-id` equals `id`). No person, governance or payment event appears in it.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"example":25,"description":"Page size. Above 100, the request is refused rather than silently truncated."},{"name":"after","in":"query","required":false,"schema":{"type":"string"},"example":"5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f","description":"Cursor: the identifier of the LAST item of the previous page. An unknown cursor is refused — it is not recovered. The three paginated lists — events, leagues, matches — follow the SAME rule: traversal is ordered on an internal insertion number that no update moves, and a page stops before any write whose transaction may still be in flight. A row written by a long transaction is therefore DELAYED, never skipped: it appears in its place as soon as its transaction commits, and exactly once. Renaming a league or correcting a match between two pages neither hides it nor brings it back."}],"responses":{"200":{"description":"A page of events.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"has_more":{"type":"boolean"}}},"example":{"object":"list","data":[{"id":"8f6e4d2c-0b9a-4c7e-a5d3-1f9e7c5a3b0d","type":"match.completed","created":"2027-01-13T03:12:45.000Z","organization":"9d3f5a7c-2b1e-4c8d-a6f0-3e2d1c0b9a87","data":{"object":"match","id":"f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c","revision":4}}],"has_more":false}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/{eventId}":{"get":{"operationId":"lireEvenement","tags":["Events"],"summary":"Reads again an event received by webhook.","description":"Scope `read`. An event outside the allowlist is NOT FOUND, not forbidden: it is not part of the public contract.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"},"example":"8f6e4d2c-0b9a-4c7e-a5d3-1f9e7c5a3b0d"}],"responses":{"200":{"description":"The event.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"},"example":{"id":"8f6e4d2c-0b9a-4c7e-a5d3-1f9e7c5a3b0d","type":"match.completed","created":"2027-01-13T03:12:45.000Z","organization":"9d3f5a7c-2b1e-4c8d-a6f0-3e2d1c0b9a87","data":{"object":"match","id":"f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c","revision":4}}}}},"400":{"description":"Malformed request (`INVALID_INPUT`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked or expired key (`UNAUTHORIZED`). Session cookies are worthless here.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope (`FORBIDDEN`), or the organization's subscription does not allow this access (`ENTITLEMENT_DENIED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in the key's organization (`NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`), per address or per key. Wait the number of seconds given by `Retry-After`, then resume with jittered exponential backoff.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unavailable. `LEAGUES_UNAVAILABLE` (outage or 10 s deadline exceeded) carries `Retry-After: 30`; a write may then be unconfirmed, so check its state before repeating it. `ENTITLEMENT_UNAVAILABLE` and `HOLDER_UNINITIALIZED`: commercial entitlement unavailable or subscription holder not initialized.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfterUnavailable"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"cleDOrganisation":{"type":"http","scheme":"bearer","bearerFormat":"rk_live_…","description":"Organization key, issued from the organization's settings by an account that governs it. The secret is shown only at issuance. Carried by the `Authorization` header ONLY: never in the query string, where it would end up in proxy logs and `Referer` headers."}},"headers":{"RequestId":{"required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_.:-]{8,64}$"},"description":"The identifier of THIS request, returned on every response, successes included, and repeated in an error's `request_id`. If you send a valid one in `X-Request-Id`, it is echoed as is; otherwise a new one is drawn."},"IdempotentReplayed":{"required":false,"schema":{"type":"string","enum":["true"]},"description":"Present, set to `true`, when the response is the replay of a request already executed with the same `Idempotency-Key`."},"RetryAfter":{"required":true,"schema":{"type":"integer","minimum":1},"description":"Seconds to wait before retrying."},"RetryAfterUnavailable":{"required":false,"schema":{"type":"integer","minimum":1},"description":"Seconds to wait before retrying: `30`, present when the code is `LEAGUES_UNAVAILABLE`."}},"schemas":{"Error":{"type":"object","required":["error","code","request_id"],"properties":{"error":{"type":"string","description":"Human-readable message, in French. Derived from the code alone: it never carries an internal identifier."},"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","NOT_FOUND","CONFLICT","INVALID_INPUT","IDEMPOTENCY_KEY_REUSED","IDEMPOTENT_REQUEST_IN_FLIGHT","IDEMPOTENCY_OUTCOME_UNKNOWN","ENTITLEMENT_DENIED","ENTITLEMENT_UNAVAILABLE","HOLDER_UNINITIALIZED","BODY_TOO_LARGE","WEBHOOK_VAULT_UNAVAILABLE","ROTATION_EN_COURS","LEAGUES_UNAVAILABLE","RATE_LIMITED"]},"request_id":{"type":"string","description":"The identifier of THIS request, also returned in the `X-Request-Id` header on every response — successes included. Copy it into a support ticket: it is what lets us find your request in our logs. If you send one yourself in `X-Request-Id`, it is echoed as is (8 to 64 characters of `A-Za-z0-9_.:-`)."}}},"SportTerm":{"type":"object","required":["one","many","genre"],"properties":{"one":{"type":"string"},"many":{"type":"string"},"genre":{"type":"string","enum":["M","F"]}}},"SportCadence":{"type":"object","required":["unitsPerMatch","minutesPerUnit","matchesPerNight","scoreTarget","seasonWeeks"],"description":"Editable profile benchmarks; null values claim no default.","properties":{"unitsPerMatch":{"type":["integer","null"],"minimum":1},"minutesPerUnit":{"type":["integer","null"],"minimum":1},"matchesPerNight":{"type":["integer","null"],"minimum":1},"scoreTarget":{"type":["integer","null"],"minimum":1},"seasonWeeks":{"type":["object","null"],"required":["min","max"],"properties":{"min":{"type":"integer","minimum":1},"max":{"type":"integer","minimum":1}}}}},"SportProfile":{"type":"object","required":["key","label","version","resourceKind","cadence","vocabulary"],"description":"The league's RESOLVED sport profile. `label` is the catalogue label, or the sport entered by the club when the key is `AUTRE`. `version` is that of the catalogue that produced this profile: it moves forward when a returned value changes, never retroactively. Labels and vocabulary are in French.","properties":{"key":{"type":"string","enum":["CURLING","CORNHOLE","HOCKEY_RECREATIF","VOLLEYBALL","PICKLEBALL","BALLE_MOLLE","AUTRE"]},"label":{"type":"string"},"version":{"type":"integer"},"resourceKind":{"type":"string","enum":["SHEET","COURT","FIELD","SURFACE"]},"cadence":{"$ref":"#/components/schemas/SportCadence"},"vocabulary":{"type":"object","required":["resource","unit","match","scoreUnit"],"description":"The sport's words: the resource a time slot books (sheet, board, field), the unit a match counts (end, inning, period), what is played and what is scored.","properties":{"resource":{"$ref":"#/components/schemas/SportTerm"},"unit":{"$ref":"#/components/schemas/SportTerm"},"match":{"$ref":"#/components/schemas/SportTerm"},"scoreUnit":{"$ref":"#/components/schemas/SportTerm"}}}}},"League":{"type":"object","required":["id","name","sport","sportLabel","profile","timeZone"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"sport":{"type":"string","enum":["CURLING","CORNHOLE","HOCKEY_RECREATIF","VOLLEYBALL","PICKLEBALL","BALLE_MOLLE","AUTRE"]},"sportLabel":{"type":["string","null"],"description":"The sport entered by the club when `sport` is `AUTRE`; `null` otherwise."},"profile":{"$ref":"#/components/schemas/SportProfile"},"timeZone":{"type":"string"}}},"LeagueDetail":{"type":"object","required":["object","id","name","sport","sportLabel","profile","timeZone","cadences","seasons"],"properties":{"object":{"type":"string","const":"league"},"id":{"type":"string"},"name":{"type":"string"},"sport":{"type":"string","enum":["CURLING","CORNHOLE","HOCKEY_RECREATIF","VOLLEYBALL","PICKLEBALL","BALLE_MOLLE","AUTRE"]},"sportLabel":{"type":["string","null"]},"profile":{"$ref":"#/components/schemas/SportProfile"},"timeZone":{"type":"string"},"cadences":{"type":"array","items":{"$ref":"#/components/schemas/Cadence"}},"seasons":{"type":"array","items":{"$ref":"#/components/schemas/Season"}}}},"Cadence":{"type":"object","required":["id","key","label","position"],"properties":{"id":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"position":{"type":"integer"}}},"CreateLeague":{"type":"object","required":["name","sport","timeZone"],"properties":{"name":{"type":"string","maxLength":200},"sport":{"type":"string","enum":["CURLING","CORNHOLE","HOCKEY_RECREATIF","VOLLEYBALL","PICKLEBALL","BALLE_MOLLE","AUTRE"],"description":"A key from the sport profile catalogue. `AUTRE` for a sport without presets."},"sportLabel":{"type":["string","null"],"maxLength":60,"description":"The sport's name, REQUIRED when `sport` is `AUTRE` and refused otherwise."},"timeZone":{"type":"string","description":"IANA time zone, for example `America/Toronto`."}}},"Season":{"type":"object","required":["id","name","sportProfile","sportProfileVersion","resourceKind","cadence","vocabulary"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"cadenceId":{"type":"string"},"ordinal":{"type":"integer"},"status":{"type":"string","enum":["SETUP","REGULAR","PLAYOFFS","CLOSED","ABANDONED"]},"sportProfile":{"type":["string","null"],"description":"Literal key frozen at creation; null before the catalogue existed. A future key is still returned."},"sportProfileVersion":{"type":["integer","null"],"minimum":1},"resourceKind":{"type":"string","enum":["SHEET","COURT","FIELD","SURFACE"]},"cadence":{"$ref":"#/components/schemas/SportCadence"},"vocabulary":{"type":"object","required":["resource","unit","match","scoreUnit"],"properties":{"resource":{"$ref":"#/components/schemas/SportTerm"},"unit":{"$ref":"#/components/schemas/SportTerm"},"match":{"$ref":"#/components/schemas/SportTerm"},"scoreUnit":{"$ref":"#/components/schemas/SportTerm"}}}}},"CreateSeason":{"type":"object","required":["name","cadenceId"],"properties":{"name":{"type":"string","maxLength":200},"cadenceId":{"type":"string"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"}}},"Category":{"type":"object","required":["id","seasonId","name","phase","revision"],"properties":{"id":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"phase":{"type":"string","enum":["SETUP","REGULAR","PLAYOFFS","CLOSED","ABANDONED"]},"revision":{"type":"integer"},"policyRevision":{"type":"integer"},"matchCount":{"type":"integer"},"unplayedMatchCount":{"type":"integer"},"unplayedRegularMatchCount":{"type":"integer"},"registrationCapacity":{"type":["integer","null"]}}},"StandingRow":{"type":"object","properties":{"leagueTeamId":{"type":"string"},"name":{"type":"string"},"rank":{"type":"integer"},"tied":{"type":"boolean"},"played":{"type":"integer"},"wins":{"type":"integer"},"draws":{"type":"integer"},"losses":{"type":"integer"},"scoreFor":{"type":"integer"},"scoreAgainst":{"type":"integer"},"diff":{"type":"integer"},"gamePointsFor":{"type":"integer","description":"Game points scored, summed over the set details. 0 outside the SETS result shape."},"gamePointsAgainst":{"type":"integer"},"points":{"type":"integer"}}},"Match":{"type":"object","required":["id","revision"],"properties":{"id":{"type":"string"},"categoryId":{"type":"string"},"status":{"type":"string"},"teamAId":{"type":["string","null"]},"teamBId":{"type":["string","null"]},"scoreA":{"type":["integer","null"]},"scoreB":{"type":["integer","null"]},"sets":{"type":["array","null"],"description":"Set details under the SETS result shape; null elsewhere, empty array on a forfeit. scoreA and scoreB then carry the SETS won.","items":{"$ref":"#/components/schemas/SetScore"}},"startsAt":{"type":["string","null"],"format":"date-time"},"revision":{"type":"integer","description":"The optimistic lock to send in `expectedRevision`."}}},"MatchResult":{"type":"object","required":["expectedRevision","event"],"properties":{"expectedRevision":{"type":"integer"},"event":{"type":"string","enum":["COMPLETER","ATTRIBUER","CORRIGER_SCORE","CORRIGER_ATTRIBUTION"]},"score":{"type":"object","properties":{"a":{"type":"integer"},"b":{"type":"integer"},"unitsPlayed":{"type":"integer"}}},"sets":{"type":"array","maxItems":15,"description":"Set details, required under the SETS result shape and refused elsewhere. The match score is not sent: it is derived from the details. A policy cannot require more than 15 sets, so no result carries more.","items":{"$ref":"#/components/schemas/SetScore"}},"resultSide":{"type":["string","null"],"enum":["A","B","DRAW",null]},"forfeitingSide":{"type":["string","null"],"enum":["A","B","BOTH",null]},"outcome":{"type":["string","null"],"enum":["PLAYED","CONCEDED","FORFEIT","DOUBLE_FORFEIT","ABANDONED",null]},"reason":{"type":["string","null"],"minLength":1,"maxLength":500}}},"SetScore":{"type":"object","required":["a","b"],"properties":{"a":{"type":"integer"},"b":{"type":"integer"}}},"Roster":{"type":"object","properties":{"registrationId":{"type":"string"},"label":{"type":"string"},"memberships":{"type":"array","items":{"type":"object"}}}},"AddMembership":{"type":"object","required":["personId"],"properties":{"personId":{"type":"string"},"role":{"type":"string","enum":["PLAYER","CAPTAIN","COACH"]},"jerseyNumber":{"type":["integer","null"],"minimum":0,"maximum":999}}},"Event":{"type":"object","required":["id","type","created","organization","data"],"properties":{"id":{"type":"string","description":"Equals the `webhook-id` header of the matching delivery."},"type":{"type":"string","enum":["league.created","season.created","season.closed","season.reopened","season.abandoned","season.renewed","category.created","category.updated","registration.submitted","registration.withdrawn","registration.reinstated","schedule.published","match.rescheduled","match.completed","match.awarded","match.corrected","match.cancelled","match.voided","series.opened","series.updated"]},"created":{"type":"string","format":"date-time"},"organization":{"type":"string"},"data":{"type":"object","description":"THIN PAYLOAD, always: an object, its identifier, its revision. Never a name, an address or a date of birth — a webhook URL is an exit point to a third party, often logged in clear in ITS logs. The consumer reads the current state again; it does not replay a six-hour-old state.","properties":{"object":{"type":"string"},"id":{"type":"string"},"revision":{"type":["integer","null"]},"previous":{"type":"object"},"scheduled":{"type":"object","description":"`match.rescheduled` ONLY — the new slot, while `previous` carries the one it replaces: `resourceId`, `startsAt`, `endsAt`. It is the only event that carries two values, because it is the only fact a consumer cannot rebuild without having kept its own state. It is emitted when the schedule is PUBLISHED, once per match whose published slot changed: a draft move was shown to no one and is therefore never announced."}}}}}}}}