{"openapi":"3.1.0","info":{"title":"Padel Lab Club API","version":"1.0.0-beta.1","summary":"Tenant-bound API for club and partner integrations","description":"The contract source for the Padel Lab Club API v1. Access tokens are bound to one active installation and one club. Unknown response fields must be ignored. Webhooks and WebSocket events are delivered at least once; consumers deduplicate by event id. All timestamps are UTC RFC 3339 values."},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","servers":[{"url":"https://api.padel-lab.crewio.co/v1/club","description":"Production"},{"url":"https://sandbox-api.padel-lab.crewio.co/v1/club","description":"Sandbox (synthetic data only)"}],"tags":[{"name":"Context"},{"name":"Locations"},{"name":"Courts"},{"name":"Booking sources"},{"name":"Bookings"},{"name":"Matches"},{"name":"Events"},{"name":"Webhooks"},{"name":"Installation"}],"security":[{"clubOAuth":[]}],"paths":{"/context":{"get":{"operationId":"getContext","tags":["Context"],"summary":"Resolve the token-bound installation and club","security":[{"clubOAuth":["club:read"]}],"responses":{"200":{"description":"Current context","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/locations":{"get":{"operationId":"listLocations","tags":["Locations"],"summary":"List club locations","security":[{"clubOAuth":["locations:read"]}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/LocationStatus"}}],"responses":{"200":{"description":"Location page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/locations/{id}":{"get":{"operationId":"getLocation","tags":["Locations"],"summary":"Get a location","security":[{"clubOAuth":["locations:read"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/courts":{"get":{"operationId":"listCourts","tags":["Courts"],"summary":"List club courts","security":[{"clubOAuth":["courts:read"]}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"name":"location_id","in":"query","schema":{"$ref":"#/components/schemas/Id"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/CourtStatus"}}],"responses":{"200":{"description":"Court page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/courts/{id}":{"get":{"operationId":"getCourt","tags":["Courts"],"summary":"Get a court","security":[{"clubOAuth":["courts:read"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Court","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/booking-sources":{"get":{"operationId":"listBookingSources","tags":["Booking sources"],"summary":"List booking sources owned by this installation","security":[{"clubOAuth":["bookings:read"]}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"Booking source page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSourceList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}},"post":{"operationId":"createBookingSource","tags":["Booking sources"],"summary":"Create a booking source","security":[{"clubOAuth":["bookings:write"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSourceInput"}}}},"responses":{"201":{"description":"Booking source created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSourceResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/booking-sources/{sourceId}/court-mappings/{externalCourtId}":{"parameters":[{"$ref":"#/components/parameters/SourceId"},{"$ref":"#/components/parameters/ExternalCourtId"}],"get":{"operationId":"getCourtMapping","tags":["Booking sources"],"summary":"Get an explicit external-to-Padel-Lab court mapping","security":[{"clubOAuth":["courts:read"]}],"responses":{"200":{"description":"Court mapping","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtMappingResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}},"put":{"operationId":"putCourtMapping","tags":["Booking sources"],"summary":"Create or replace an explicit court mapping","description":"No heuristic court matching is performed. The selected court must belong to the token-bound club.","security":[{"clubOAuth":["bookings:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtMappingInput"}}}},"responses":{"200":{"description":"Court mapping saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtMappingResponse"}}}},"201":{"description":"Court mapping created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourtMappingResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/bookings":{"get":{"operationId":"listBookings","tags":["Bookings"],"summary":"List booking snapshots","description":"Participant objects are returned only when the token also has participants:read and consent checks pass. Source-bound external ids are exposed only to the installation that owns that source. from includes rows with scheduled_start greater than or equal to the value; to includes rows with scheduled_start less than or equal to the value. When both are present, from must be less than or equal to to.","security":[{"clubOAuth":["bookings:read"]}],"x-conditional-scopes":[{"scope":"participants:read","condition":"Include consented, source-bound participant objects in the response; without it participants is an empty array."}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"name":"location_id","in":"query","schema":{"$ref":"#/components/schemas/Id"}},{"name":"court_id","in":"query","schema":{"$ref":"#/components/schemas/Id"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/BookingStatus"}},{"name":"provider","in":"query","schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"from","in":"query","schema":{"$ref":"#/components/schemas/Timestamp"}},{"name":"to","in":"query","schema":{"$ref":"#/components/schemas/Timestamp"}}],"responses":{"200":{"description":"Booking page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/bookings/{id}":{"get":{"operationId":"getBooking","tags":["Bookings"],"summary":"Get a booking snapshot","description":"The participants array is empty unless the token also has participants:read and every returned participant has explicit consent and belongs to this installation's booking source.","security":[{"clubOAuth":["bookings:read"]}],"x-conditional-scopes":[{"scope":"participants:read","condition":"Include consented, source-bound participant objects in the response; without it participants is an empty array."}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/booking-sources/{sourceId}/bookings/{externalBookingId}":{"put":{"operationId":"putBooking","tags":["Bookings"],"summary":"Create or update the complete source-owned booking snapshot","description":"source_version must increase monotonically. The same version and same content is an idempotent replay; the same version with different content or an older version returns VERSION_CONFLICT. Cancellation uses status=CANCELLED. If an operational match can no longer be changed, the booking snapshot is accepted and a reconciliation conflict is created without overwriting Padel Lab match state. Supplying participants requires participants:write; response participants additionally require participants:read and explicit source-bound consent.","security":[{"clubOAuth":["bookings:write"]}],"x-conditional-scopes":[{"scope":"participants:write","condition":"The request body contains the participants property."},{"scope":"participants:read","condition":"Include consented, source-bound participant objects in the response; without it participants is an empty array."}],"parameters":[{"$ref":"#/components/parameters/SourceId"},{"$ref":"#/components/parameters/ExternalBookingId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSnapshotInput"}}}},"responses":{"200":{"description":"Booking updated or exact version replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMutationResponse"}}}},"201":{"description":"Booking created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMutationResponse"}}}},"202":{"description":"Snapshot accepted; reconciliation is required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMutationResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/booking-sources/{sourceId}/imports":{"post":{"operationId":"createBookingImport","tags":["Bookings"],"summary":"Queue an initial import of at most 500 booking snapshots","security":[{"clubOAuth":["bookings:write"]}],"x-conditional-scopes":[{"scope":"participants:write","condition":"At least one imported booking contains the participants property."}],"parameters":[{"$ref":"#/components/parameters/SourceId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingImportInput"}}}},"responses":{"201":{"description":"Import job created and queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingImportAcceptedResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/booking-imports/{id}":{"get":{"operationId":"getBookingImport","tags":["Bookings"],"summary":"Get import progress and per-record results","security":[{"clubOAuth":["bookings:read"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Import job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingImportResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/match-sessions":{"get":{"operationId":"listMatchSessions","tags":["Matches"],"summary":"List Padel Lab-owned match sessions","description":"This resource is read-only. Participant objects are returned only with participants:read and successful consent checks; score is returned only with scores:read. from includes rows with scheduled_start greater than or equal to the value; to includes rows with scheduled_start less than or equal to the value. When both are present, from must be less than or equal to to.","security":[{"clubOAuth":["sessions:read"]}],"x-conditional-scopes":[{"scope":"participants:read","condition":"Include consented, source-bound participant objects in the response; without it participants is an empty array."},{"scope":"scores:read","condition":"Include the current allow-listed score snapshot; without it score is null."}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"name":"location_id","in":"query","schema":{"$ref":"#/components/schemas/Id"}},{"name":"court_id","in":"query","schema":{"$ref":"#/components/schemas/Id"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/MatchSessionStatus"}},{"name":"from","in":"query","schema":{"$ref":"#/components/schemas/Timestamp"}},{"name":"to","in":"query","schema":{"$ref":"#/components/schemas/Timestamp"}}],"responses":{"200":{"description":"Match session page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchSessionList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/match-sessions/{id}":{"get":{"operationId":"getMatchSession","tags":["Matches"],"summary":"Get a match session","description":"The participants array is empty unless the token also has participants:read and every returned participant has explicit consent and belongs to this installation's booking source. Score is null unless the token also has scores:read.","security":[{"clubOAuth":["sessions:read"]}],"x-conditional-scopes":[{"scope":"participants:read","condition":"Include consented, source-bound participant objects in the response; without it participants is an empty array."},{"scope":"scores:read","condition":"Include the current allow-listed score snapshot; without it score is null."}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Match session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchSessionResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/match-sessions/{id}/score-events":{"get":{"operationId":"listScoreEvents","tags":["Matches"],"summary":"List immutable score events","security":[{"clubOAuth":["scores:read"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"Score event page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreEventList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/match-sessions/{id}/score-events/{eventId}":{"get":{"operationId":"getScoreEvent","tags":["Matches"],"summary":"Get one score event","security":[{"clubOAuth":["scores:read"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/ScoreEventId"}],"responses":{"200":{"description":"Score event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreEventResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/events":{"get":{"operationId":"listEvents","tags":["Events"],"summary":"Replay retained events or upgrade to a WebSocket stream","description":"Without ticket this is HTTP replay. For WebSocket, connect with wss and a one-use ticket query parameter; Authorization is not sent on the upgrade. Tickets expire after 60 seconds, connections after 60 minutes, and retained events after seven days. Delivery is at least once.","security":[{"clubOAuth":["events:read"]}],"parameters":[{"name":"after","in":"query","description":"Previously processed event id","schema":{"$ref":"#/components/schemas/EventId"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"x-websocket":{"subprotocol":null,"security":[{"eventStreamTicket":[]}],"authentication":{"httpReplay":"Bearer access token with events:read","upgrade":"One-use ticket query parameter created by POST /event-stream-tickets; no Authorization header is sent on the upgrade"},"ticketParameter":{"name":"ticket","in":"query","required":true,"schema":{"type":"string","minLength":40,"maxLength":100}},"maxConnectionsPerInstallation":5,"maxLifetimeSeconds":3600,"resumeWith":"event.id","serverFrames":["Event","connected","heartbeat","replay_required"],"messageSchemas":[{"$ref":"#/components/schemas/Event"},{"$ref":"#/components/schemas/WebSocketConnected"},{"$ref":"#/components/schemas/WebSocketHeartbeat"},{"$ref":"#/components/schemas/WebSocketReplayRequired"}],"closeCodes":{"1000":"Maximum lifetime reached; reconnect with replay and a fresh ticket","1008":"Ticket is invalid, expired, already consumed or lacks events:read","1011":"Temporary stream failure","1013":"Five-connection installation or club limit reached"}},"responses":{"101":{"description":"WebSocket protocol switched when a valid ticket is supplied"},"200":{"description":"Event replay page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/event-stream-tickets":{"post":{"operationId":"createEventStreamTicket","tags":["Events"],"summary":"Create a one-use 60-second WebSocket ticket","security":[{"clubOAuth":["events:read"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventStreamTicketInput"}}}},"responses":{"201":{"description":"Ticket created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventStreamTicketResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/webhook-endpoints":{"get":{"operationId":"listWebhookEndpoints","tags":["Webhooks"],"summary":"List this installation's webhook endpoints","security":[{"clubOAuth":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"Webhook endpoint page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointList"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}},"post":{"operationId":"createWebhookEndpoint","tags":["Webhooks"],"summary":"Create a webhook endpoint","description":"Only HTTPS public-network URLs are accepted. Redirects are not followed. signing_secret is returned only in this response (and an exact idempotent replay). Maximum ten endpoints per installation and per club.","security":[{"clubOAuth":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointInput"}}}},"responses":{"201":{"description":"Webhook endpoint created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointSecretResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/webhook-endpoints/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"patch":{"operationId":"updateWebhookEndpoint","tags":["Webhooks"],"summary":"Update a webhook endpoint or its subscriptions","security":[{"clubOAuth":["webhooks:manage"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointPatch"}}}},"responses":{"200":{"description":"Webhook endpoint updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}},"delete":{"operationId":"deleteWebhookEndpoint","tags":["Webhooks"],"summary":"Disable a webhook endpoint","description":"This is a soft disable: the endpoint remains visible to management reads with status DISABLED. event_types is empty until the endpoint is reactivated.","security":[{"clubOAuth":["webhooks:manage"]}],"responses":{"204":{"description":"Webhook endpoint disabled"},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/webhook-endpoints/{id}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","tags":["Webhooks"],"summary":"Rotate a webhook signing secret","description":"The new secret is returned only in this response and an exact idempotent replay.","security":[{"clubOAuth":["webhooks:manage"]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Secret rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotationResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/oauth/installation-exchange":{"post":{"operationId":"exchangePartnerInstallationCode","tags":["Installation"],"summary":"Exchange a one-time partner installation authorization code","description":"Server-side exchange for an authorization code issued by the Club Portal. The exact redirect_uri and PKCE S256 verifier must match the owner-approved request. A successful exchange provisions one environment- and club-bound ZITADEL M2M principal; it never returns a private key or browser token.","security":[],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerInstallationExchangeInput"}}}},"responses":{"200":{"description":"Installation principal provisioned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerInstallationExchangeResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}},"webhooks":{"clubEvent":{"post":{"operationId":"receiveClubEvent","summary":"Receive one Padel Lab event","description":"Verify Padel-Lab-Signature as v1=<lowercase hex HMAC-SHA-256> over timestamp + '.' + the exact raw request body. Reject stale timestamps. Delivery is at least once; deduplicate by event id. Retries occur immediately, then after 1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours, 24 hours and 48 hours.","parameters":[{"name":"Padel-Lab-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"}},{"name":"Padel-Lab-Signature","in":"header","required":true,"schema":{"type":"string","pattern":"^v1=[a-f0-9]{64}$"}},{"name":"Padel-Lab-Delivery","in":"header","required":true,"schema":{"$ref":"#/components/schemas/Id"}},{"name":"Padel-Lab-Event","in":"header","required":true,"schema":{"$ref":"#/components/schemas/EventType"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"responses":{"200":{"description":"Event accepted. Any 2xx response is treated as success."}}}}},"components":{"securitySchemes":{"clubOAuth":{"type":"oauth2","description":"ZITADEL M2M access token obtained with an RS256 JWT-bearer assertion. The clientCredentials-shaped flow declares the API's scope vocabulary for OpenAPI tooling; token acquisition follows the x-zitadel-jwt-bearer profile below. Tokens are short-lived and bound server-side to one active installation and club. Browser and mobile clients must use their own backend.","flows":{"clientCredentials":{"tokenUrl":"https://identity.padel-lab.crewio.co/oauth/v2/token","scopes":{"club:read":"Read the token-bound club context","locations:read":"Read locations","courts:read":"Read courts and mappings","bookings:read":"Read bookings, sources and imports","bookings:write":"Write source-owned bookings, sources and mappings","participants:read":"Read consented display names and source-bound external ids","participants:write":"Send participants before operational match preparation","sessions:read":"Read match sessions","scores:read":"Read score state and score events","events:read":"Replay and stream events","webhooks:manage":"Manage webhook endpoints and subscriptions"}}},"x-zitadel-jwt-bearer":{"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion_parameter":"assertion","assertion_algorithm":"RS256","assertion_audience":"ZITADEL issuer","required_token_scopes":["openid","installation-specific API audience scope"]},"x-environment-token-urls":{"PRODUCTION":"https://identity.padel-lab.crewio.co/oauth/v2/token","SANDBOX":"https://sandbox-identity.padel-lab.crewio.co/oauth/v2/token","authoritativeSource":"The successful installation-exchange response for the selected environment"}},"eventStreamTicket":{"type":"apiKey","in":"query","name":"ticket","description":"One-use, 60-second ticket used only for the WebSocket upgrade. Obtain it from POST /event-stream-tickets."}},"parameters":{"Limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},"Cursor":{"name":"cursor","in":"query","description":"Opaque cursor returned by the previous page","schema":{"type":"string","maxLength":1024,"pattern":"^[A-Za-z0-9_-]*$"}},"ResourceId":{"name":"id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Id"}},"EventId":{"name":"eventId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EventId"}},"ScoreEventId":{"name":"eventId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Id"}},"SourceId":{"name":"sourceId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Id"}},"ExternalCourtId":{"name":"externalCourtId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}},"ExternalBookingId":{"name":"externalBookingId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key retained for 24 hours on authenticated mutation endpoints. The installation-exchange binding is retained permanently with its consumed authorization code. Reuse with different request content returns IDEMPOTENCY_CONFLICT.","schema":{"type":"string","minLength":8,"maxLength":200,"pattern":"^[A-Za-z0-9._:-]+$"}}},"headers":{"XRequestId":{"description":"Request correlation id","schema":{"type":"string"}},"RateLimitLimit":{"description":"Applicable request limit","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests remaining in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time when the current window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds before retrying","schema":{"type":"integer"}}},"responses":{"InvalidRequest":{"description":"Malformed JSON or a syntactically valid request that violates a path, query, header or JSON-body constraint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Missing, invalid or expired token","headers":{"WWW-Authenticate":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Forbidden":{"description":"Installation lacks the required scope or is inactive","headers":{"WWW-Authenticate":{"description":"Present for insufficient_scope responses","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource is absent or outside the token-bound tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Conflict":{"description":"Version, idempotency, mapping, state or resource-limit conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unavailable":{"description":"A required authorization, tenant-safety or rate-limit dependency is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalError":{"description":"An unexpected runtime error occurred; retry only when the operation is safe or idempotent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"Id":{"type":"string","minLength":1,"maxLength":200,"description":"Opaque stable identifier"},"EventId":{"type":"string","format":"uuid"},"Timestamp":{"type":"string","format":"date-time","description":"UTC RFC 3339 timestamp"},"NullableTimestamp":{"type":["string","null"],"format":"date-time"},"PublicSigningJwk":{"type":"object","additionalProperties":false,"required":["kty","n","e","alg","use"],"properties":{"kty":{"const":"RSA"},"n":{"type":"string","pattern":"^[A-Za-z0-9_-]{342,}$"},"e":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,16}$"},"alg":{"const":"RS256"},"use":{"const":"sig"},"kid":{"type":"string","minLength":1,"maxLength":200}}},"PartnerInstallationExchangeInput":{"type":"object","additionalProperties":false,"required":["grant_type","code","code_verifier","redirect_uri"],"properties":{"grant_type":{"const":"authorization_code"},"code":{"type":"string","pattern":"^[A-Za-z0-9_-]{40,200}$"},"code_verifier":{"type":"string","minLength":43,"maxLength":128,"pattern":"^[A-Za-z0-9._~-]+$"},"redirect_uri":{"type":"string","format":"uri","maxLength":2048}}},"PartnerInstallationExchangeResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"object","additionalProperties":false,"required":["installation_id","organization_id","application_id","environment","scopes","oauth_subject","credential_key_id","nonce","api_base_url","oauth_issuer","oauth_token_url","oauth_audience","oauth_audience_scope"],"properties":{"installation_id":{"$ref":"#/components/schemas/Id"},"organization_id":{"$ref":"#/components/schemas/Id"},"application_id":{"$ref":"#/components/schemas/Id"},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiScope"}},"oauth_subject":{"type":"string"},"credential_key_id":{"type":"string","minLength":1},"nonce":{"type":"string"},"api_base_url":{"type":"string","format":"uri","description":"Environment-specific Club API base URL. This exchange response is authoritative."},"oauth_issuer":{"type":"string","format":"uri","description":"Environment-specific ZITADEL issuer used as the JWT assertion audience."},"oauth_token_url":{"type":"string","format":"uri","description":"Environment-specific ZITADEL token endpoint."},"oauth_audience":{"type":"string","minLength":1,"description":"Audience required on Club API access tokens."},"oauth_audience_scope":{"type":"string","pattern":"^urn:zitadel:iam:org:project:id:[A-Za-z0-9_-]+:aud$","description":"ZITADEL project audience scope sent alongside openid in the token request."}}}}},"LocationStatus":{"type":"string","enum":["ACTIVE","MAINTENANCE","DISABLED"]},"CourtStatus":{"type":"string","enum":["AVAILABLE","BOOKED_SOON","SESSION_ACTIVE","MAINTENANCE","DEVICE_WARNING","OFFLINE","DISABLED"]},"BookingStatus":{"type":"string","enum":["CONFIRMED","CANCELLED","COMPLETED"]},"MatchSessionStatus":{"type":"string","enum":["SCHEDULED","READY","WAITING_FOR_PLAYERS","ACTIVE","PAUSED","COMPLETED","CANCELLED","ABORTED"]},"ParticipantTeam":{"type":"string","enum":["BLUE","ORANGE","UNASSIGNED"]},"ApiScope":{"type":"string","enum":["club:read","locations:read","courts:read","bookings:read","bookings:write","participants:read","participants:write","sessions:read","scores:read","events:read","webhooks:manage"]},"EventType":{"type":"string","enum":["booking.created","booking.updated","booking.cancelled","booking.reconciliation_required","session.created","session.status_changed","participant.updated","match.started","score.updated","score.corrected","match.completed","court.status_changed"]},"Error":{"type":"object","additionalProperties":false,"required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Stable machine-readable code. Clients must tolerate unknown codes within the same HTTP class.","examples":["invalid_request","invalid_cursor","IDEMPOTENCY_KEY_REQUIRED","unsafe_webhook_url","authentication_required","invalid_token","insufficient_scope","installation_inactive","not_found","VERSION_CONFLICT","UNMAPPED_COURT","IDEMPOTENCY_CONFLICT","IDEMPOTENCY_IN_PROGRESS","endpoint_limit_reached","rate_limited","internal_error","idempotency_replay_failed","authorization_unavailable","rate_limit_unavailable"]},"message":{"type":"string"},"request_id":{"type":"string"},"details":{}}},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"$ref":"#/components/schemas/Error"}}},"PageMetadata":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}},"Context":{"type":"object","additionalProperties":false,"required":["installation","application","club"],"properties":{"installation":{"type":"object","additionalProperties":false,"required":["id","application_id","environment","status","scopes","installed_at","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"application_id":{"$ref":"#/components/schemas/Id"},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]},"status":{"type":"string","enum":["ACTIVE"]},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiScope"},"uniqueItems":true},"installed_at":{"$ref":"#/components/schemas/Timestamp"},"expires_at":{"$ref":"#/components/schemas/NullableTimestamp"}}},"application":{"type":"object","additionalProperties":false,"required":["id","slug","name"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"slug":{"type":"string"},"name":{"type":"string"}}},"club":{"type":"object","additionalProperties":false,"required":["id","slug","name","status","timezone","currency","country","language"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"slug":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"timezone":{"type":"string"},"currency":{"type":"string"},"country":{"type":"string"},"language":{"type":"string"}}}}},"ContextResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Context"}}},"Location":{"type":"object","additionalProperties":false,"required":["id","name","address","city","postal_code","country","timezone","status","opening_hours","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"name":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string"},"timezone":{"type":"string"},"status":{"$ref":"#/components/schemas/LocationStatus"},"opening_hours":{},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"LocationResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Location"}}},"LocationList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}]},"Court":{"type":"object","additionalProperties":false,"required":["id","location_id","name","number","public_id","status","indoor_outdoor","surface_type","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"location_id":{"$ref":"#/components/schemas/Id"},"name":{"type":"string"},"number":{"type":"integer"},"public_id":{"type":"string"},"status":{"$ref":"#/components/schemas/CourtStatus"},"indoor_outdoor":{"type":"string","enum":["INDOOR","OUTDOOR","COVERED"]},"surface_type":{"type":"string"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"CourtResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Court"}}},"CourtList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Court"}}}}]},"BookingSource":{"type":"object","additionalProperties":false,"required":["id","source_key","name","provider","status","external_account_id","last_synchronized_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"source_key":{"type":"string"},"name":{"type":"string"},"provider":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","PAUSED","ERROR","DISABLED"]},"external_account_id":{"type":["string","null"]},"last_synchronized_at":{"$ref":"#/components/schemas/NullableTimestamp"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"BookingSourceInput":{"type":"object","additionalProperties":false,"required":["source_key","name"],"properties":{"source_key":{"type":"string","minLength":2,"maxLength":80,"pattern":"^[a-z0-9][a-z0-9._-]+$"},"name":{"type":"string","minLength":2,"maxLength":120,"x-runtime-normalization":"trim-before-length-validation"},"external_account_id":{"type":["string","null"],"maxLength":200,"x-runtime-normalization":"trim-before-length-validation"}}},"BookingSourceResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BookingSource"}}},"BookingSourceList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BookingSource"}}}}]},"CourtMapping":{"type":"object","additionalProperties":false,"required":["id","external_court_id","external_court_name","court_id","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"external_court_id":{"type":"string"},"external_court_name":{"type":["string","null"]},"court_id":{"$ref":"#/components/schemas/Id"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"CourtMappingInput":{"type":"object","additionalProperties":false,"required":["court_id","external_court_name"],"properties":{"court_id":{"$ref":"#/components/schemas/Id"},"external_court_name":{"type":"string","minLength":1,"maxLength":120,"x-runtime-normalization":"trim-before-length-validation"}}},"CourtMappingResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CourtMapping"}}},"ParticipantInput":{"type":"object","additionalProperties":false,"required":["external_id","display_name"],"properties":{"external_id":{"type":"string","minLength":1,"maxLength":200,"x-runtime-normalization":"trim-before-length-validation"},"display_name":{"type":"string","minLength":1,"maxLength":80,"x-runtime-normalization":"trim-before-length-validation"},"team":{"$ref":"#/components/schemas/ParticipantTeam"},"consent_to_share":{"type":"boolean","default":false,"description":"Explicit consent to expose the data-minimized participant through the Club API. Omission is treated as false."},"consent_reference":{"type":"string","minLength":1,"maxLength":200,"description":"Optional source-side consent receipt or policy reference.","x-runtime-normalization":"trim-before-length-validation"}},"allOf":[{"if":{"required":["consent_reference"]},"then":{"required":["consent_to_share"],"properties":{"consent_to_share":{"const":true}}}}]},"Participant":{"type":"object","additionalProperties":false,"required":["external_id","display_name"],"description":"Data-minimized, explicitly consented participant. The response contains only source-bound external_id and display_name, and only when consent_to_share was true, the caller has participants:read and the participant belongs to the caller's source. Team, position, confirmation state, email, raw payloads, media, statistics and unrelated personal attributes are never included.","properties":{"external_id":{"type":"string"},"display_name":{"type":"string"}}},"BookingSnapshotInput":{"type":"object","additionalProperties":false,"description":"A complete source-owned snapshot. scheduled_end must be after scheduled_start and the duration must not exceed 24 hours.","required":["source_version","external_court_id","scheduled_start","scheduled_end","status"],"properties":{"source_version":{"type":"integer","minimum":0,"maximum":2147483647},"external_court_id":{"type":"string","minLength":1,"maxLength":200,"x-runtime-normalization":"trim-before-length-validation"},"scheduled_start":{"$ref":"#/components/schemas/Timestamp"},"scheduled_end":{"$ref":"#/components/schemas/Timestamp"},"status":{"$ref":"#/components/schemas/BookingStatus"},"participants":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/ParticipantInput"},"description":"Requires participants:write in addition to bookings:write."}}},"Booking":{"type":"object","additionalProperties":false,"required":["id","booking_source_id","location_id","court_id","provider","external_booking_id","external_court_id","source_version","scheduled_start","scheduled_end","status","participants","match_session_id","imported_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"booking_source_id":{"type":["string","null"]},"location_id":{"$ref":"#/components/schemas/Id"},"court_id":{"$ref":"#/components/schemas/Id"},"provider":{"type":"string"},"external_booking_id":{"type":["string","null"]},"external_court_id":{"type":"string"},"source_version":{"type":["integer","null"]},"scheduled_start":{"$ref":"#/components/schemas/Timestamp"},"scheduled_end":{"$ref":"#/components/schemas/Timestamp"},"status":{"$ref":"#/components/schemas/BookingStatus"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/Participant"}},"match_session_id":{"type":["string","null"]},"imported_at":{"$ref":"#/components/schemas/Timestamp"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"BookingResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Booking"}}},"BookingList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}]},"BookingMutationResponse":{"type":"object","additionalProperties":false,"required":["data","reconciliation_status","idempotent_replay"],"properties":{"data":{"$ref":"#/components/schemas/Booking"},"reconciliation_status":{"type":"string","enum":["none","required"]},"idempotent_replay":{"type":"boolean"}}},"BookingImportItem":{"type":"object","additionalProperties":false,"required":["external_booking_id","booking"],"properties":{"external_booking_id":{"type":"string","minLength":1,"maxLength":200},"booking":{"$ref":"#/components/schemas/BookingSnapshotInput"}}},"BookingImportInput":{"type":"object","additionalProperties":false,"required":["bookings"],"properties":{"bookings":{"type":"array","minItems":1,"maxItems":500,"items":{"$ref":"#/components/schemas/BookingImportItem"}}}},"BookingImportAccepted":{"type":"object","additionalProperties":false,"required":["id","status","total","imported","failed","created_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"status":{"type":"string","enum":["PENDING"]},"total":{"type":"integer","minimum":1,"maximum":500},"imported":{"type":"integer","minimum":0},"failed":{"type":"integer","minimum":0},"created_at":{"$ref":"#/components/schemas/Timestamp"}}},"BookingImportAcceptedResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BookingImportAccepted"}}},"BookingImport":{"type":"object","additionalProperties":false,"required":["id","booking_source_id","status","total","imported","failed","result","started_at","completed_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"booking_source_id":{"$ref":"#/components/schemas/Id"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","PARTIAL","FAILED"]},"total":{"type":"integer","minimum":0},"imported":{"type":"integer","minimum":0},"failed":{"type":"integer","minimum":0},"result":{},"started_at":{"$ref":"#/components/schemas/NullableTimestamp"},"completed_at":{"$ref":"#/components/schemas/NullableTimestamp"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"BookingImportResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BookingImport"}}},"MatchParticipant":{"type":"object","additionalProperties":false,"required":["display_name","external_id"],"description":"Data-minimized participant identity containing only the source-bound external id and display name.","properties":{"display_name":{"type":"string"},"external_id":{"type":"string"}}},"ScoreStringPair":{"type":"object","additionalProperties":false,"required":["blue","orange"],"properties":{"blue":{"type":"string"},"orange":{"type":"string"}}},"ScoreNumberPair":{"type":"object","additionalProperties":false,"required":["blue","orange"],"properties":{"blue":{"type":"integer","minimum":0},"orange":{"type":"integer","minimum":0}}},"ScoreSnapshot":{"type":"object","additionalProperties":false,"required":["points","games","sets","set_history","tie_break","match_tie_break","point_number","star_deuce_count","changeover_count","deciding_point_side","completed","winner","last_transition"],"properties":{"points":{"$ref":"#/components/schemas/ScoreStringPair"},"games":{"$ref":"#/components/schemas/ScoreNumberPair"},"sets":{"$ref":"#/components/schemas/ScoreNumberPair"},"set_history":{"type":"array","items":{"$ref":"#/components/schemas/ScoreNumberPair"}},"tie_break":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ScoreNumberPair"}]},"match_tie_break":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ScoreNumberPair"}]},"point_number":{"type":"integer","minimum":0},"star_deuce_count":{"type":"integer","minimum":0},"changeover_count":{"type":"integer","minimum":0},"deciding_point_side":{"type":["string","null"],"enum":["RIGHT","LEFT",null]},"completed":{"type":"boolean"},"winner":{"type":["string","null"],"enum":["BLUE","ORANGE",null]},"last_transition":{"type":"string","enum":["POINT","GAME","SET","MATCH"]}}},"MatchSession":{"type":"object","additionalProperties":false,"required":["id","location_id","court_id","scheduled_start","scheduled_end","actual_start","actual_end","status","booking","scoring_mode","display_phase","event_sequence","rule_version","ai_model_version","score","participants","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"location_id":{"$ref":"#/components/schemas/Id"},"court_id":{"$ref":"#/components/schemas/Id"},"scheduled_start":{"$ref":"#/components/schemas/Timestamp"},"scheduled_end":{"$ref":"#/components/schemas/Timestamp"},"actual_start":{"$ref":"#/components/schemas/NullableTimestamp"},"actual_end":{"$ref":"#/components/schemas/NullableTimestamp"},"status":{"$ref":"#/components/schemas/MatchSessionStatus"},"booking":{"oneOf":[{"type":"null"},{"type":"object","additionalProperties":false,"required":["provider","external_booking_id"],"properties":{"provider":{"type":"string"},"external_booking_id":{"type":["string","null"]}}}]},"scoring_mode":{"type":"string"},"display_phase":{"type":"string"},"event_sequence":{"type":"integer","minimum":0},"rule_version":{"type":"string"},"ai_model_version":{"type":["string","null"]},"score":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ScoreSnapshot"}]},"participants":{"type":"array","items":{"$ref":"#/components/schemas/MatchParticipant"}},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"MatchSessionResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MatchSession"}}},"MatchSessionList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MatchSession"}}}}]},"ScoreEvent":{"type":"object","additionalProperties":false,"required":["id","match_session_id","event_type","team","source","snapshot_before","snapshot_after","reversed_event_id","client_event_id","sequence","created_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"match_session_id":{"$ref":"#/components/schemas/Id"},"event_type":{"type":"string"},"team":{"type":["string","null"]},"source":{"type":"string"},"snapshot_before":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ScoreSnapshot"}]},"snapshot_after":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ScoreSnapshot"}]},"reversed_event_id":{"type":["string","null"]},"client_event_id":{"type":["string","null"]},"sequence":{"type":["integer","null"]},"created_at":{"$ref":"#/components/schemas/Timestamp"}}},"ScoreEventResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ScoreEvent"}}},"ScoreEventList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScoreEvent"}}}}]},"EventResource":{"type":"object","additionalProperties":false,"required":["type","id"],"properties":{"type":{"type":"string"},"id":{"$ref":"#/components/schemas/Id"}}},"Event":{"type":"object","additionalProperties":true,"required":["id","type","schema_version","occurred_at","publication_sequence","organization_id","resource","sequence","changed_fields","data_url"],"properties":{"id":{"$ref":"#/components/schemas/EventId"},"type":{"$ref":"#/components/schemas/EventType"},"schema_version":{"type":"integer","minimum":1},"occurred_at":{"$ref":"#/components/schemas/Timestamp"},"publication_sequence":{"type":"string","pattern":"^[1-9][0-9]*$","maxLength":19,"description":"Canonical organization-local publication order encoded as a decimal string so values remain exact beyond the JSON safe-integer range."},"organization_id":{"$ref":"#/components/schemas/Id"},"resource":{"$ref":"#/components/schemas/EventResource"},"sequence":{"type":"integer","minimum":1},"changed_fields":{"type":"array","items":{"type":"string"},"uniqueItems":true},"data_url":{"type":"string"}}},"EventList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}}}]},"EventStreamTicketInput":{"type":"object","additionalProperties":false,"properties":{"after":{"type":["string","null"],"format":"uuid"}}},"EventStreamTicket":{"type":"object","additionalProperties":false,"required":["ticket","expires_at","websocket_url"],"properties":{"ticket":{"type":"string"},"expires_at":{"$ref":"#/components/schemas/Timestamp"},"websocket_url":{"type":"string"}}},"EventStreamTicketResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/EventStreamTicket"}}},"WebSocketConnected":{"type":"object","additionalProperties":false,"required":["type","organization_id","installation_id","at"],"properties":{"type":{"const":"connected"},"organization_id":{"$ref":"#/components/schemas/Id"},"installation_id":{"$ref":"#/components/schemas/Id"},"at":{"$ref":"#/components/schemas/Timestamp"}}},"WebSocketHeartbeat":{"type":"object","additionalProperties":false,"required":["type","at"],"properties":{"type":{"const":"heartbeat"},"at":{"$ref":"#/components/schemas/Timestamp"}}},"WebSocketReplayRequired":{"type":"object","additionalProperties":false,"required":["type","after"],"properties":{"type":{"const":"replay_required"},"after":{"$ref":"#/components/schemas/EventId"}}},"WebhookEndpointInput":{"type":"object","additionalProperties":false,"required":["name","url","event_types"],"properties":{"name":{"type":"string","minLength":2,"maxLength":120,"x-runtime-normalization":"trim-before-length-validation"},"url":{"type":"string","format":"uri","maxLength":2048,"pattern":"^[Hh][Tt][Tt][Pp][Ss]://","description":"Public HTTPS URL without user information or a fragment. Private and reserved networks are rejected after DNS resolution."},"description":{"type":["string","null"],"maxLength":500,"x-runtime-normalization":"trim-before-length-validation"},"event_types":{"type":"array","minItems":1,"maxItems":12,"items":{"$ref":"#/components/schemas/EventType"},"description":"Duplicate values are accepted and normalized to one subscription."}}},"WebhookEndpointPatch":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":2,"maxLength":120,"x-runtime-normalization":"trim-before-length-validation"},"url":{"type":"string","format":"uri","maxLength":2048,"pattern":"^[Hh][Tt][Tt][Pp][Ss]://","description":"Public HTTPS URL without user information or a fragment. Private and reserved networks are rejected after DNS resolution."},"description":{"type":["string","null"],"maxLength":500,"x-runtime-normalization":"trim-before-length-validation"},"status":{"type":"string","enum":["ACTIVE","DISABLED"]},"event_types":{"type":"array","minItems":1,"maxItems":12,"items":{"$ref":"#/components/schemas/EventType"},"description":"Duplicate values are accepted and normalized to one subscription."}}},"WebhookEndpoint":{"type":"object","required":["id","name","url","description","status","event_types","signing_secret_version","consecutive_failures","last_failure_at","disabled_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ACTIVE","DISABLED"]},"event_types":{"type":"array","items":{"$ref":"#/components/schemas/EventType"},"uniqueItems":true},"signing_secret_version":{"type":"integer","minimum":1},"consecutive_failures":{"type":"integer","minimum":0},"last_failure_at":{"$ref":"#/components/schemas/NullableTimestamp"},"disabled_at":{"$ref":"#/components/schemas/NullableTimestamp"},"created_at":{"$ref":"#/components/schemas/Timestamp"},"updated_at":{"$ref":"#/components/schemas/Timestamp"}}},"WebhookEndpointResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"WebhookEndpointList":{"allOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}]},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["signing_secret"],"properties":{"signing_secret":{"type":"string","readOnly":true}}}]},"WebhookEndpointSecretResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}},"WebhookSecretRotation":{"type":"object","additionalProperties":false,"required":["id","signing_secret","signing_secret_version"],"properties":{"id":{"$ref":"#/components/schemas/Id"},"signing_secret":{"type":"string","readOnly":true},"signing_secret_version":{"type":"integer","minimum":2}}},"WebhookSecretRotationResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookSecretRotation"}}}}},"x-padel-lab":{"stability":"private-beta","partnerInstallation":{"profile":"padel-lab-installation-code-pkce-v1","description":"A proprietary installation-consent protocol, not a standard OAuth token endpoint. The authenticated Club Portal authorizes an application_id with exact redirect URI, state, nonce, PKCE S256 and an RSA public JWK. The partner backend exchanges the one-use code as JSON; the response provisions an M2M principal but does not contain an access token.","authorizationUrl":"https://club.padel-lab.crewio.co/developer/install","environmentAuthorizationUrls":{"PRODUCTION":"https://club.padel-lab.crewio.co/developer/install","SANDBOX":"https://sandbox-club.padel-lab.crewio.co/developer/install"},"exchangePath":"/oauth/installation-exchange","exchangeContentType":"application/json","codeChallengeMethods":["S256"],"returnsAccessToken":false},"pagination":{"defaultLimit":50,"maximumLimit":100,"cursor":"opaque"},"idempotencyRetentionSeconds":86400,"eventRetentionSeconds":604800,"rateLimits":{"requestsPerMinute":600,"writesPerMinute":120,"scope":["installation","club"]},"webhookFailureDisableThreshold":20,"errorCodes":{"400":["invalid_request","invalid_cursor","invalid_grant","IDEMPOTENCY_KEY_REQUIRED","unsafe_webhook_url"],"401":["authentication_required","invalid_token"],"403":["insufficient_scope","installation_inactive"],"404":["not_found"],"409":["VERSION_CONFLICT","UNMAPPED_COURT","IDEMPOTENCY_CONFLICT","IDEMPOTENCY_IN_PROGRESS","endpoint_limit_reached","installation_exists","installation_state_changed"],"429":["rate_limited"],"500":["internal_error","idempotency_replay_failed"],"503":["authorization_unavailable","rate_limit_unavailable","service_unavailable"]},"breakingChangeDeprecationMonthsAfterStableV1":12}}