PUT
/booking-sources/{sourceId}/bookings/{externalBookingId}Create or update the complete source-owned booking snapshot
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.
Bearer Token
bookings:writeparticipants:writeThe request body contains the participants property.participants:readInclude consented, source-bound participant objects in the response; without it participants is an empty array.Request
curl --request PUT \
'https://sandbox-api.padel-lab.crewio.co/v1/club/booking-sources/src_01JABC/bookings/booking-1001' \
--header 'Authorization: Bearer $PADEL_LAB_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"source_version": 42,
"status": "CONFIRMED",
"external_court_id": "court-1",
"scheduled_start": "2030-01-15T18:00:00.000Z",
"scheduled_end": "2030-01-15T19:30:00.000Z",
"participants": [
{
"external_id": "player-17",
"display_name": "Alex",
"consent_to_share": true,
"consent_reference": "consent-2030-001"
}
]
}'Parameter
sourceIdpathIdrequiredexternalBookingIdpathstringrequiredRequest Body
BookingSnapshotInputrequired{
"source_version": 42,
"status": "CONFIRMED",
"external_court_id": "court-1",
"scheduled_start": "2030-01-15T18:00:00.000Z",
"scheduled_end": "2030-01-15T19:30:00.000Z",
"participants": [
{
"external_id": "player-17",
"display_name": "Alex",
"consent_to_share": true,
"consent_reference": "consent-2030-001"
}
]
}JSON Schema anzeigen
{
"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": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"scheduled_end": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"status": {
"type": "string",
"enum": [
"CONFIRMED",
"CANCELLED",
"COMPLETED"
]
},
"participants": {
"type": "array",
"maxItems": 4,
"items": {
"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": {
"type": "string",
"enum": [
"BLUE",
"ORANGE",
"UNASSIGNED"
]
},
"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
}
}
}
}
]
},
"description": "Requires participants:write in addition to bookings:write."
}
}
}Responses
200Booking updated or exact version replayedBookingMutationResponse201Booking createdBookingMutationResponse202Snapshot accepted; reconciliation is requiredBookingMutationResponse400Malformed JSON or a syntactically valid request that violates a path, query, header or JSON-body constraint.ErrorResponse401Missing, invalid or expired tokenErrorResponse403Installation lacks the required scope or is inactiveErrorResponse404Resource is absent or outside the token-bound tenantErrorResponse409Version, idempotency, mapping, state or resource-limit conflictErrorResponse429Rate limit exceededErrorResponse500An unexpected runtime error occurred; retry only when the operation is safe or idempotent.ErrorResponse503A required authorization, tenant-safety or rate-limit dependency is unavailableErrorResponseResponse Schemas
BookingMutationResponseStatus 200, 201, 202
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"reconciliation_status",
"idempotent_replay"
],
"properties": {
"data": {
"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": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"booking_source_id": {
"type": [
"string",
"null"
]
},
"location_id": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"court_id": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"provider": {
"type": "string"
},
"external_booking_id": {
"type": [
"string",
"null"
]
},
"external_court_id": {
"type": "string"
},
"source_version": {
"type": [
"integer",
"null"
]
},
"scheduled_start": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"scheduled_end": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"status": {
"type": "string",
"enum": [
"CONFIRMED",
"CANCELLED",
"COMPLETED"
]
},
"participants": {
"type": "array",
"items": {
"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"
}
}
}
},
"match_session_id": {
"type": [
"string",
"null"
]
},
"imported_at": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "UTC RFC 3339 timestamp"
}
}
},
"reconciliation_status": {
"type": "string",
"enum": [
"none",
"required"
]
},
"idempotent_replay": {
"type": "boolean"
}
}
}ErrorResponseStatus 400, 401, 403, 404, 409, 429, 500, 503
{
"type": "object",
"additionalProperties": false,
"required": [
"error"
],
"properties": {
"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": {}
}
}
}
}