{"openapi":"3.0.3","info":{"title":"SeatsChart API","description":"Seat inventory and locking API for B2B2C integrations. Authenticate using `x-api-key` (partners) or `Authorization: Bearer` with an HS256 JWT.","version":"1.0.0"},"tags":[{"name":"billing","description":"Stripe subscription and plan limits"},{"name":"usage","description":"Monthly used objects (distinct objects booked per UTC month), chart/event counts and plan allowances. Rate limits are per tenant and plan, with a reserved sales lane: see `X-RateLimit-Tenant-Policy` and `Retry-After` on `429`."},{"name":"events","description":"Events, their inventory and reports. Every `{eventId}` path parameter (and body `eventId` / `eventIds` / `events`) also accepts the event `key` (unique per workspace); UUIDs are never looked up."},{"name":"charts","description":"Reusable seating charts: drafts, validation, categories, publish history, copies and tags."},{"name":"object-status","description":"Administrative object status changes (seats and general-admission quantities): book, hold, release, retire, resale, custom statuses and extra data."},{"name":"listings","description":"Server-side listings (resale and primary) and listing types: grouped objects sold whole or one by one, with prices, colors and live viewer updates."},{"name":"workspace","description":"Workspace profile, membership list and activation (dashboard sessions)."},{"name":"sandboxes","description":"Isolated sandboxes of a production workspace: their own charts, events, holds, orders, ticket buyer ids, webhooks and `sk_test_` keys; never billed; resettable. Managed from the production workspace only."},{"name":"orders","description":"Durable orders and whole-order cancellation."},{"name":"public","description":"Unauthenticated, CDN-cacheable reads keyed by the workspace public key (`/api/public/{publicKey}/…`). Default public cohort only: no channel inventory, holder identities or ops fields."},{"name":"sse","description":"Server-Sent Events for live seat updates"},{"name":"seats","description":"Reservation and Lua locks"},{"name":"webhooks","description":"Partner webhook endpoints: per-endpoint HMAC secrets (rotation), event-type subscriptions, a delivery log covering the durable outbox and direct sends, replay with the current secret and signed test events."},{"name":"api-keys","description":"Tenant-scoped secret API keys (`sk_live_…`, SHA-256 at rest) with scopes (read, holds, bookings, inventory_admin, charts_write, events_write, reports, webhooks); dashboard JWT lifecycle only. Missing scope → 403 `insufficient_scope`."},{"name":"auth","description":"Dashboard signup, login, and session profile (email/password accounts)."},{"name":"audit","description":"Workspace configuration change history (admin JWT only)."},{"name":"seasons","description":"Seasons and partial seasons: member events, season status and overrides, bookings and for-sale propagation, season reports."},{"name":"event-log","description":"Event log of admin changes to charts and events (cursor-paginated, retention per workspace)."},{"name":"team","description":"Workspace members, roles and invitations (dashboard sessions)."},{"name":"embed","description":"Short-lived viewer, designer and Event Manager embed tokens, and registered ticket buyer ids."},{"name":"embed-origins","description":"Allowed embed origins per workspace and the resolved embed policy of an event or season."},{"name":"quotes","description":"Server-calculated catalog quotes frozen before an external payment."},{"name":"map-designer","description":"Map designer helpers: seat candidates extracted from a floor-plan image (OpenCV, no AI cost)."},{"name":"assets","description":"Public, cacheable map assets (designer reference and background images, sponsor billboard images)."},{"name":"health","description":"Liveness, readiness and metrics probes for operators."}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"HS256 JWT; optional `tenant_id` claim (falls back to DEFAULT_TENANT_ID). SSE clients must send `Authorization: Bearer` (fetch-based); query-string tokens are rejected."},"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Partner secret (`sk_live_…`) looked up via SHA-256 in Postgres (`api_keys`); optional bootstrap `API_KEY` / `API_KEYS` in env are bound to DEFAULT_TENANT_ID only (client `x-tenant-id` is ignored for bootstrap keys)."}},"schemas":{"ApiError":{"type":"object","required":["error","message"],"description":"Every error response. Match on `error` (stable catalog code, see docs ERROR_CODES.md); `code` is an optional detail code for localized messages; `message` is English text for logs. Kinds: validation (400/413/422), auth (401/403), not_found (404), conflict (409/410/412), payment (402), rate_limit (429), server (500/502/503). Deprecated aliases (no longer returned; normalize old comparisons): `validation` → `validation_error`. The `x-request-id` response header identifies the request for support.","properties":{"error":{"type":"string","enum":["ambiguous_label","ambiguous_season_key","bad_request","booking_limit_exceeded","buyer_limit_exceeded","category_exists","category_in_use","channel_forbidden","chart_archived","chart_invalid","chart_key_taken","chart_not_published","chart_switch_conflict","checkout_failed","checkout_not_complete","confirm_failed","conflict","currency_mismatch","draft_conflict","draft_not_found","empty_chart","event_key_taken","event_not_found","event_sale","extra_data_not_published","extraction_failed","for_sale_edit_conflict","forbidden","ga_area_occupancy_locked","ga_area_whole","ga_places_to_channels_disabled","ga_table_whole","geometry_not_published","hold_token_conflict","idempotency_conflict","idempotency_key_required","illegal_previous_status","insufficient_free_places","insufficient_inventory","insufficient_scope","internal_error","invalid_body","invalid_code","invalid_order_id","invalid_parameters","invalid_signature","inventory_conflict","last_active_workspace","limit_exceeded","listing_closed","listing_has_holds","listing_incomplete","listing_key_taken","listing_object_conflict","listing_objects_removed","listing_type_exists","listing_type_in_use","lot_sold_out","no_customer","no_subscription","not_found","not_published","not_season_member","objects_not_found","own_workspace","partial_season_key_taken","payload_too_large","pending_sign_in_expired","plan_limit_exceeded","portal_failed","price_mismatch","price_table_missing","quantity_required","quota_exceeded","quote_conflict","quote_mismatch","quote_required","rate_limit_exceeded","redis_unavailable","sandbox_limit_reached","scan_route_retired","season_booked","season_bookings_not_propagated","season_chart_mismatch","season_has_no_chart","season_key_taken","season_source_event","season_source_required","season_status_conflict","season_status_overridden","service_draining","service_unavailable","setup_expired","stripe_not_configured","structural_mismatch","test_workspace_not_billable","ticket_buyer_id_required","ticket_buyer_mismatch","ticket_buyer_not_registered","ticket_buyer_required","ticket_type_required","too_many_attempts","too_many_events","two_factor_already_enabled","two_factor_enrollment_required","two_factor_not_enabled","two_factor_required_by_workspace","unauthorized","validation_error","validation_failed","webhook_handler_failed","workspace_deactivated"],"description":"Stable machine-readable code (catalog)."},"message":{"type":"string","description":"Human-readable English message; never match on it."},"code":{"type":"string","enum":["ERR_BAD_REQUEST","ERR_NOT_FOUND","ERR_UNAUTHORIZED","ERR_FORBIDDEN","ERR_CONFLICT","ERR_VALIDATION","ERR_SERVICE_UNAVAILABLE","ERR_INTERNAL","ERR_RATE_LIMIT","ERR_PLAN_LIMIT","ERR_IDEMPOTENCY_CONFLICT","ERR_SEAT_LOCKED","ERR_SEAT_UNAVAILABLE","ERR_SEAT_SOLD","ERR_SEAT_HOLD_REQUIRED","SEAT_NOT_FOR_SALE","SEAT_CHANNEL_FORBIDDEN","BA_NO_HOLDABLE_WINDOW","ERR_SEATS_NOT_FOUND","ERR_EVENT_NOT_FOUND","ERR_CHART_NOT_FOUND","ERR_CHART_NOT_ARCHIVED","ERR_CHART_HAS_EVENTS","ERR_AUTO_PUBLISH_CHART_HAS_EVENTS","ERR_CHART_TAG_INVALID","ERR_CHART_TAG_LIMIT","ERR_CHART_VERSION_NOT_FOUND","ERR_MAP_VERSION_NOT_FOUND","ERR_MAP_NEVER_PUBLISHED","ERR_DRAFT_NOTHING_TO_DISCARD","ERR_THUMBNAIL_EXISTS","ERR_THUMBNAIL_INVALID","ERR_NAME_EMPTY","ERR_GA_HOLD_INVALID","ERR_GA_HOLD_MISSING","ERR_GA_CAPACITY","ERR_GA_AREA_NOT_FOUND","GA_NOT_FOR_SALE","GA_CHANNEL_FORBIDDEN","ERR_HOLD_SERVICE_UNAVAILABLE","ERR_PUBSUB_FAILED","ERR_CHANNEL_LIMIT","SEASON_SOURCE_REQUIRED","SEASON_CHART_MISMATCH","SEASON_EVENT_ALREADY_MEMBER","SEASON_MEMBER_CAP","ERR_SEASON_NOT_FOUND","ERR_PARTNER_SCOPE","ERR_FORBIDDEN_ROLE","ERR_SANDBOX_SCOPE","ERR_EMBED_SCOPE","ERR_DESIGNER_READ_ONLY","ERR_DESIGNER_SAFE_MODE"],"description":"Optional detail code (UPPER_SNAKE)."},"params":{"type":"object","additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"}]}}},"additionalProperties":true,"x-error-kinds":{"ambiguous_label":"conflict","ambiguous_season_key":"conflict","bad_request":"validation","booking_limit_exceeded":"conflict","buyer_limit_exceeded":"conflict","category_exists":"conflict","category_in_use":"conflict","channel_forbidden":"auth","chart_archived":"conflict","chart_invalid":"conflict","chart_key_taken":"conflict","chart_not_published":"conflict","chart_switch_conflict":"conflict","checkout_failed":"server","checkout_not_complete":"conflict","confirm_failed":"server","conflict":"conflict","currency_mismatch":"conflict","draft_conflict":"conflict","draft_not_found":"not_found","empty_chart":"validation","event_key_taken":"conflict","event_not_found":"not_found","event_sale":"conflict","extra_data_not_published":"not_found","extraction_failed":"server","for_sale_edit_conflict":"validation","forbidden":"auth","ga_area_occupancy_locked":"conflict","ga_area_whole":"validation","ga_places_to_channels_disabled":"auth","ga_table_whole":"validation","geometry_not_published":"conflict","hold_token_conflict":"conflict","idempotency_conflict":"conflict","idempotency_key_required":"validation","illegal_previous_status":"conflict","insufficient_free_places":"conflict","insufficient_inventory":"conflict","insufficient_scope":"auth","internal_error":"server","invalid_body":"validation","invalid_code":"validation","invalid_order_id":"validation","invalid_parameters":"validation","invalid_signature":"validation","inventory_conflict":"conflict","last_active_workspace":"conflict","limit_exceeded":"conflict","listing_closed":"conflict","listing_has_holds":"conflict","listing_incomplete":"conflict","listing_key_taken":"conflict","listing_object_conflict":"conflict","listing_objects_removed":"conflict","listing_type_exists":"conflict","listing_type_in_use":"conflict","lot_sold_out":"conflict","no_customer":"payment","no_subscription":"payment","not_found":"not_found","not_published":"conflict","not_season_member":"validation","objects_not_found":"validation","own_workspace":"conflict","partial_season_key_taken":"conflict","payload_too_large":"validation","pending_sign_in_expired":"auth","plan_limit_exceeded":"payment","portal_failed":"server","price_mismatch":"conflict","price_table_missing":"conflict","quantity_required":"validation","quota_exceeded":"payment","quote_conflict":"conflict","quote_mismatch":"conflict","quote_required":"conflict","rate_limit_exceeded":"rate_limit","redis_unavailable":"server","sandbox_limit_reached":"conflict","scan_route_retired":"conflict","season_booked":"conflict","season_bookings_not_propagated":"conflict","season_chart_mismatch":"conflict","season_has_no_chart":"conflict","season_key_taken":"conflict","season_source_event":"conflict","season_source_required":"conflict","season_status_conflict":"conflict","season_status_overridden":"conflict","service_draining":"server","service_unavailable":"server","setup_expired":"validation","stripe_not_configured":"server","structural_mismatch":"conflict","test_workspace_not_billable":"payment","ticket_buyer_id_required":"validation","ticket_buyer_mismatch":"auth","ticket_buyer_not_registered":"auth","ticket_buyer_required":"auth","ticket_type_required":"validation","too_many_attempts":"rate_limit","too_many_events":"conflict","two_factor_already_enabled":"conflict","two_factor_enrollment_required":"auth","two_factor_not_enabled":"conflict","two_factor_required_by_workspace":"auth","unauthorized":"auth","validation_error":"validation","validation_failed":"validation","webhook_handler_failed":"server","workspace_deactivated":"auth"},"x-deprecated-error-aliases":{"validation":"validation_error"}}}},"paths":{"/health/live":{"get":{"operationId":"getHealthLive","tags":["health"],"summary":"Liveness probe","description":"Answers 200 `{ status: \"ok\" }` while the process serves requests, with no Redis or PostgreSQL check (use `GET /api/health` for readiness). Still served while the API drains on shutdown.","responses":{"200":{}}}},"/internal/metrics":{"get":{"operationId":"getInternalMetrics","tags":["health"],"summary":"Prometheus metrics","description":"Prometheus text exposition (`text/plain; version=0.0.4`) of the API process metrics. Requires `Authorization: Bearer <OBSERVABILITY_TOKEN>` (at least 32 characters); any other request, or an API without the token configured, gets 404 `not_found`.","responses":{"200":{}}}},"/assets/map/{tenantId}/{eventId}/{filename}":{"get":{"operationId":"getAssetsMapByTenantIdByEventIdByFilename","tags":["assets"],"summary":"Serve persisted map reference/background images","description":"Serves map images from local disk or proxies from R2. Production requires HMAC `exp`/`sig` unless MAP_ASSET_REQUIRE_SIGNED=0.","parameters":[{"schema":{"type":"string"},"in":"path","name":"tenantId","required":true},{"schema":{"type":"string"},"in":"path","name":"eventId","required":true},{"schema":{"type":"string"},"in":"path","name":"filename","required":true}],"responses":{"200":{}}}},"/internal/status/notices":{"put":{"parameters":[],"operationId":"putInternalStatusNotices","tags":["health"],"summary":"Replace status page notices (operators)","description":"Replaces the manual incident and maintenance notices shown on the public status page (at most 20, English text). An incident with `endsAt` in the past is resolved; a maintenance window is scheduled, in progress or completed from its times. Requires `Authorization: Bearer <OBSERVABILITY_TOKEN>`; any other request gets 404 `not_found`. Send `{ \"notices\": [] }` to clear.","requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["notices"],"properties":{"notices":{"maxItems":20,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","kind","title","startsAt"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","type":"string"},"kind":{"anyOf":[{"const":"incident","type":"string"},{"const":"maintenance","type":"string"}]},"title":{"minLength":1,"maxLength":160,"type":"string"},"message":{"maxLength":2000,"type":"string"},"impact":{"anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"components":{"maxItems":7,"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["notices"],"properties":{"notices":{"maxItems":20,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","kind","title","startsAt"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","type":"string"},"kind":{"anyOf":[{"const":"incident","type":"string"},{"const":"maintenance","type":"string"}]},"title":{"minLength":1,"maxLength":160,"type":"string"},"message":{"maxLength":2000,"type":"string"},"impact":{"anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"components":{"maxItems":7,"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["notices"],"properties":{"notices":{"maxItems":20,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","kind","title","startsAt"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","type":"string"},"kind":{"anyOf":[{"const":"incident","type":"string"},{"const":"maintenance","type":"string"}]},"title":{"minLength":1,"maxLength":160,"type":"string"},"message":{"maxLength":2000,"type":"string"},"impact":{"anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"components":{"maxItems":7,"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}}}}}}},"responses":{"200":{}}}},"/api/billing/webhook":{"post":{"operationId":"postApiBillingWebhook","tags":["billing"],"summary":"Stripe webhook","description":"Receives Stripe subscription lifecycle events. Requires STRIPE_WEBHOOK_SECRET and raw body signature verification.","responses":{"200":{}}}},"/api/events/{eventId}/embed-policy":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdEmbed-policy","tags":["embed-origins"],"summary":"Resolve tenant embed origins for an event","description":"Public read used by the Next.js viewer to build CSP frame-ancestors and validate parent_origin. Also returns `publicKey` (`pub_…`) so `?workspaceKey=` can be allowlisted. Also returns `event` (`name`, `startsAt` ISO-8601 or null when the date was never set) for the buyer page header. Also returns `extraData`: true when the workspace publishes allow-listed extraData keys to buyers (the viewer then reads `GET /api/public/{publicKey}/events/{eventId}/extra-data`). Also returns `rendererDefaults`: the default renderer settings of the workspace (only the options it sets: `multiSelectEnabled`, `showFullScreenButton`, `companionSeatValidationEnabled`, `holdOnSelectForGAs`; see `GET /api/workspace/defaults`), from the cached workspace config (no query). An explicit embed / host option wins over them. Also returns `maxSeatsPerHoldToken`, the seats one hold token may hold (workspace setting, default 64; above 64 the workspace opts into large group holds and the viewer lets buyers select and hold up to that many seats in one atomic call). Returns origins, public key and the event name/date only (no tenant id). Does not require authentication.","responses":{"200":{}}}},"/api/seasons/{seasonId}/embed-policy":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"seasonId","required":true}],"operationId":"getApiSeasonsBySeasonIdEmbed-policy","tags":["embed-origins"],"summary":"Resolve tenant embed origins for a season","description":"Public read used by the Next.js season viewer (`/viewer/season/{seasonId}`) to build CSP frame-ancestors from the season workspace's embed origins and to validate `parent_origin` and `?workspaceKey=`, like the event embed policy. Also returns `rendererDefaults`: the default renderer settings of the workspace (only the options it sets: `multiSelectEnabled`, `showFullScreenButton`, `companionSeatValidationEnabled`, `holdOnSelectForGAs`; see `GET /api/workspace/defaults`), from the cached workspace config (no query). An explicit embed / host option wins over them. Also returns `maxSeatsPerHoldToken`, the seats one hold token may hold (workspace setting, default 64; above 64 the workspace opts into large group holds and the viewer lets buyers select and hold up to that many seats in one atomic call). Returns origins, the public key and the renderer defaults only (no tenant id). Unknown seasons return 404 (the viewer then allows only `ALLOWED_ORIGINS`). Does not require authentication.","responses":{"200":{}}}},"/api/public/{publicKey}/events/{eventId}/manifest":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiPublicByPublicKeyEventsByEventIdManifest","tags":["public"],"summary":"Public geometry manifest (CDN-cacheable)","security":[],"description":"Published geometry pointer for the event. `geometryUrl` points at the public geometry path. ETag = mapRevision + currency. Includes the event catalog `currency` (ISO 4217, null for legacy events). `Cache-Control: public, max-age=5, stale-while-revalidate=30` (PUBLIC_MANIFEST_* env). A stale manifest is safe: the state carries mapRevision and clients switch geometry when it differs. Unknown key or event: 404 no-store. `eventId` also accepts the event key. A deactivated workspace returns 403 `workspace_deactivated` (no-store). CDN cache key: path + Origin + normalized Accept-Encoding. Never forward or key on cookies, Authorization, x-api-key, x-channel-key or the query string.","responses":{"200":{}}}},"/api/public/{publicKey}/events/{eventId}/geometry/{hash}":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"in":"path","name":"hash","required":true}],"operationId":"getApiPublicByPublicKeyEventsByEventIdGeometryByHash","tags":["public"],"summary":"Public immutable geometry (CDN-cacheable)","security":[],"description":"Content-addressed geometry (sha256 of the canonical JSON). br/gzip negotiated from Accept-Encoding. `Cache-Control: public, max-age=31536000, immutable`. Clients must verify the hash. Unknown key, event or hash: 404 no-store. `eventId` also accepts the event key. A deactivated workspace returns 403 `workspace_deactivated` (no-store). CDN cache key: path + Origin + normalized Accept-Encoding. Never forward or key on cookies, Authorization, x-api-key, x-channel-key or the query string.","responses":{"200":{}}}},"/api/public/{publicKey}/events/{eventId}/state":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiPublicByPublicKeyEventsByEventIdState","tags":["public"],"summary":"Public compact visual state (CDN-cacheable)","security":[],"description":"Compact availability of the default public cohort (channel-restricted objects are reported as not for sale; no holder identities or ops fields). Coalesced per API process (single-flight + micro-cache validated by the inventory version, <= 1 s). Weak ETag = stateToken + epoch.sequence; If-None-Match returns 304. `Cache-Control: public, max-age=1, s-maxage=2, stale-while-revalidate=5` (PUBLIC_STATE_* env). Live changes continue on the authenticated SSE stream; reserve/confirm remain authoritative. `eventId` also accepts the event key. A deactivated workspace returns 403 `workspace_deactivated` (no-store). CDN cache key: path + Origin + normalized Accept-Encoding. Never forward or key on cookies, Authorization, x-api-key, x-channel-key or the query string.","responses":{"200":{}}}},"/api/public/{publicKey}/events/{eventId}/extra-data":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiPublicByPublicKeyEventsByEventIdExtra-data","tags":["public"],"summary":"Published buyer extraData of an event (workspace opt-in, CDN-cacheable)","security":[],"description":"Object `extraData` is private by default. When a workspace admin enables buyer publication (`PUT /api/workspace/buyer-extra-data`) with an allow-list of keys, this read returns, for the workspace's events, `{ keys, objects: { [objectId]: { key: value } } }` with only those keys and scalar values (strings up to 256 characters, numbers, booleans, null; objects, arrays and longer strings are never published). The renderer reads it once after the map for declarative rules (`match.extraData`), templates (`{extraData.key}`) and host objects. Built from one indexed query on a miss, then served from a per-event cache dropped whenever the seat map cache is invalidated (overlay writes, extraData updates) and rebuilt when the key list changes. Never part of the compact state. Not published (setting off or no keys): 404 `extra_data_not_published`. ETag; `If-None-Match` returns 304. `Cache-Control: public, max-age=5, stale-while-revalidate=10`. `eventId` also accepts the event key.","responses":{"200":{}}}},"/api/public/chart-previews/{chartId}/manifest":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Expiry (unix seconds) from the link.","schema":{"type":"string","pattern":"^[0-9]{1,12}$"},"in":"query","name":"exp","required":true},{"description":"Signature from the link.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"sig","required":true}],"operationId":"getApiPublicChart-previewsByChartIdManifest","tags":["public"],"summary":"Chart preview manifest (signed link)","security":[],"description":"Validates a preview link and returns `{ chartId, name, mapRevision, geometryHash, seatCount, gaCount, expiresAt, geometryUrl }`. 403 `preview_link_invalid` (bad signature or revoked), 410 `preview_link_expired`, 404 unknown/unpublished/archived chart. `Cache-Control: public, max-age=30`; `X-Robots-Tag: noindex`.","responses":{"200":{}}}},"/api/public/chart-previews/{chartId}/geometry/{hash}":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"in":"path","name":"hash","required":true},{"description":"Expiry (unix seconds) from the link.","schema":{"type":"string","pattern":"^[0-9]{1,12}$"},"in":"query","name":"exp","required":true},{"description":"Signature from the link.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"sig","required":true}],"operationId":"getApiPublicChart-previewsByChartIdGeometryByHash","tags":["public"],"summary":"Chart preview geometry (signed link)","security":[],"description":"Content-addressed published geometry (sha256 of the canonical JSON; clients verify it). Structural fields only — no prices, inventory or event data. Cacheable while the link is valid (max one day).","responses":{"200":{}}}},"/api/public/{publicKey}/charts/{chart}/embed-policy":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"description":"Chart id or chart key.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$"},"in":"path","name":"chart","required":true}],"operationId":"getApiPublicByPublicKeyChartsByChartEmbed-policy","tags":["public"],"summary":"Map-only embed policy of a chart","security":[],"description":"Used by the Next.js map-only viewer (`/viewer/chart/{chart}?workspaceKey=…`) to build CSP frame-ancestors and validate `parent_origin`, like the event embed policy: `{ origins, chart: { id, name }, rendererDefaults }`. No tenant id. A deactivated workspace returns 403 `workspace_deactivated`; an unknown key or chart, a chart that was never published or is archived: 404 `not_found` (errors are `no-store`). CDN cache key: path + Origin + normalized Accept-Encoding.","responses":{"200":{}}}},"/api/public/{publicKey}/charts/{chart}/manifest":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"description":"Chart id or chart key.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$"},"in":"path","name":"chart","required":true}],"operationId":"getApiPublicByPublicKeyChartsByChartManifest","tags":["public"],"summary":"Map-only chart manifest (CDN-cacheable)","security":[],"description":"Published chart for a map-only render (seats.io `bringYourOwnBackend`): `{ chartId, name, eventId (= chartId), mapRevision, geometryHash, geometrySchema, seatCount, gaCount, geometryUrl, state }`. `state` is the compact state with every object free (catalog prices, GA capacities, category prices / ticket types, booking rules); the renderer overlays the statuses the host passes. No inventory, holds or event data. Every object of the chart (seats + general admission areas) counts as a used object once per chart and UTC month (see the map-only guide). ETag; `If-None-Match` returns 304. `Cache-Control: public, max-age=30, stale-while-revalidate=60`. `chart` accepts the chart id or key. A deactivated workspace returns 403 `workspace_deactivated`; an unknown key or chart, a chart that was never published or is archived: 404 `not_found` (errors are `no-store`). CDN cache key: path + Origin + normalized Accept-Encoding.","responses":{"200":{}}}},"/api/public/{publicKey}/charts/{chart}/geometry/{hash}":{"get":{"parameters":[{"description":"Workspace public key (`pub_…`).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"publicKey","required":true},{"description":"Chart id or chart key.","schema":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$"},"in":"path","name":"chart","required":true},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"in":"path","name":"hash","required":true}],"operationId":"getApiPublicByPublicKeyChartsByChartGeometryByHash","tags":["public"],"summary":"Map-only chart geometry (immutable, CDN-cacheable)","security":[],"description":"Content-addressed published geometry of the chart (sha256 of the canonical JSON; clients verify it), the same document as the chart preview geometry. `Cache-Control: public, max-age=31536000, immutable`. Unknown hash: 404. A deactivated workspace returns 403 `workspace_deactivated`; an unknown key or chart, a chart that was never published or is archived: 404 `not_found` (errors are `no-store`). CDN cache key: path + Origin + normalized Accept-Encoding.","responses":{"200":{}}}},"/api/health":{"get":{"operationId":"getApiHealth","tags":["health"],"summary":"Health check","description":"Readiness probe with Redis, PostgreSQL and release schema checks (503 when degraded).","responses":{"200":{}}}},"/api/status":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"generatedAt":{"format":"date-time","type":"string"},"historyDays":{"description":"Days in every `days` array (90).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Days in every `days` array (90).","type":"integer"}]},"sampleIntervalSeconds":{"description":"Sampler interval; null before the first sample.","anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]},"lastSampleAt":{"description":"Time of the latest sample; null when nothing was sampled yet.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"stale":{"description":"True when the latest sample is older than three intervals (at least 3 minutes): the sampler is not running and current states are `unknown`.","type":"boolean"},"overall":{"anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"partial_outage","type":"string"},{"const":"major_outage","type":"string"},{"const":"maintenance","type":"string"},{"const":"unknown","type":"string"}]},"components":{"type":"array","items":{"type":"object","required":["id","status","latencyMs","uptime","days"],"properties":{"id":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]},"status":{"description":"`not_monitored` when the component has no probe configured on this deployment.","anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"outage","type":"string"},{"const":"unknown","type":"string"},{"const":"not_monitored","type":"string"}]},"latencyMs":{"description":"Latest probe round trip in ms (for `queue`: age of the oldest unprocessed job).","anyOf":[{"type":"number"},{"type":"null"}]},"uptime":{"description":"Uptime over the sampled minutes of the 90 days; null without samples.","anyOf":[{"type":"number"},{"type":"null"}]},"days":{"description":"Oldest first; the last entry is today (UTC).","type":"array","items":{"type":"object","required":["date","uptime","coverage","degradedMinutes","downMinutes","incidents"],"properties":{"date":{"description":"UTC day (`YYYY-MM-DD`).","type":"string"},"uptime":{"description":"Percent of the sampled minutes that were up (degraded counts as up), rounded down to 2 decimals; null when the day has no sample (\"no data\", never \"up\").","anyOf":[{"type":"number"},{"type":"null"}]},"coverage":{"description":"Share of the day (so far, for today) with a sample: 0–1. Below 1 the sampler was not running part of the day.","type":"number"},"degradedMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"downMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"incidents":{"description":"Incidents (automatic and manual) that overlap the day.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Incidents (automatic and manual) that overlap the day.","type":"integer"}]}}}}}}},"incidents":{"description":"Newest first, last 90 days, at most 50.","type":"array","items":{"type":"object","required":["id","source","components","severity","title","message","startedAt","resolvedAt"],"properties":{"id":{"type":"string"},"source":{"description":"`automatic`: derived from three consecutive failed samples (resolved after three good ones); `manual`: posted by an operator.","anyOf":[{"const":"automatic","type":"string"},{"const":"manual","type":"string"}]},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"severity":{"description":"`major` when the component was down, `minor` when degraded.","anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"title":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"message":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"resolvedAt":{"description":"Null while the incident is open.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"maintenance":{"description":"Scheduled and in-progress windows, and those completed in the last 7 days.","type":"array","items":{"type":"object","required":["id","title","message","components","startsAt","endsAt","state"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"description":"Null when open-ended.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"state":{"anyOf":[{"const":"scheduled","type":"string"},{"const":"in_progress","type":"string"},{"const":"completed","type":"string"}]}}}}},"required":["generatedAt","historyDays","sampleIntervalSeconds","lastSampleAt","stale","overall","components","incidents","maintenance"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"generatedAt":{"format":"date-time","type":"string"},"historyDays":{"description":"Days in every `days` array (90).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Days in every `days` array (90).","type":"integer"}]},"sampleIntervalSeconds":{"description":"Sampler interval; null before the first sample.","anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]},"lastSampleAt":{"description":"Time of the latest sample; null when nothing was sampled yet.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"stale":{"description":"True when the latest sample is older than three intervals (at least 3 minutes): the sampler is not running and current states are `unknown`.","type":"boolean"},"overall":{"anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"partial_outage","type":"string"},{"const":"major_outage","type":"string"},{"const":"maintenance","type":"string"},{"const":"unknown","type":"string"}]},"components":{"type":"array","items":{"type":"object","required":["id","status","latencyMs","uptime","days"],"properties":{"id":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]},"status":{"description":"`not_monitored` when the component has no probe configured on this deployment.","anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"outage","type":"string"},{"const":"unknown","type":"string"},{"const":"not_monitored","type":"string"}]},"latencyMs":{"description":"Latest probe round trip in ms (for `queue`: age of the oldest unprocessed job).","anyOf":[{"type":"number"},{"type":"null"}]},"uptime":{"description":"Uptime over the sampled minutes of the 90 days; null without samples.","anyOf":[{"type":"number"},{"type":"null"}]},"days":{"description":"Oldest first; the last entry is today (UTC).","type":"array","items":{"type":"object","required":["date","uptime","coverage","degradedMinutes","downMinutes","incidents"],"properties":{"date":{"description":"UTC day (`YYYY-MM-DD`).","type":"string"},"uptime":{"description":"Percent of the sampled minutes that were up (degraded counts as up), rounded down to 2 decimals; null when the day has no sample (\"no data\", never \"up\").","anyOf":[{"type":"number"},{"type":"null"}]},"coverage":{"description":"Share of the day (so far, for today) with a sample: 0–1. Below 1 the sampler was not running part of the day.","type":"number"},"degradedMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"downMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"incidents":{"description":"Incidents (automatic and manual) that overlap the day.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Incidents (automatic and manual) that overlap the day.","type":"integer"}]}}}}}}},"incidents":{"description":"Newest first, last 90 days, at most 50.","type":"array","items":{"type":"object","required":["id","source","components","severity","title","message","startedAt","resolvedAt"],"properties":{"id":{"type":"string"},"source":{"description":"`automatic`: derived from three consecutive failed samples (resolved after three good ones); `manual`: posted by an operator.","anyOf":[{"const":"automatic","type":"string"},{"const":"manual","type":"string"}]},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"severity":{"description":"`major` when the component was down, `minor` when degraded.","anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"title":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"message":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"resolvedAt":{"description":"Null while the incident is open.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"maintenance":{"description":"Scheduled and in-progress windows, and those completed in the last 7 days.","type":"array","items":{"type":"object","required":["id","title","message","components","startsAt","endsAt","state"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"description":"Null when open-ended.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"state":{"anyOf":[{"const":"scheduled","type":"string"},{"const":"in_progress","type":"string"},{"const":"completed","type":"string"}]}}}}},"required":["generatedAt","historyDays","sampleIntervalSeconds","lastSampleAt","stale","overall","components","incidents","maintenance"]}},"text/plain":{"schema":{"type":"object","properties":{"generatedAt":{"format":"date-time","type":"string"},"historyDays":{"description":"Days in every `days` array (90).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Days in every `days` array (90).","type":"integer"}]},"sampleIntervalSeconds":{"description":"Sampler interval; null before the first sample.","anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]},"lastSampleAt":{"description":"Time of the latest sample; null when nothing was sampled yet.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"stale":{"description":"True when the latest sample is older than three intervals (at least 3 minutes): the sampler is not running and current states are `unknown`.","type":"boolean"},"overall":{"anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"partial_outage","type":"string"},{"const":"major_outage","type":"string"},{"const":"maintenance","type":"string"},{"const":"unknown","type":"string"}]},"components":{"type":"array","items":{"type":"object","required":["id","status","latencyMs","uptime","days"],"properties":{"id":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]},"status":{"description":"`not_monitored` when the component has no probe configured on this deployment.","anyOf":[{"const":"operational","type":"string"},{"const":"degraded","type":"string"},{"const":"outage","type":"string"},{"const":"unknown","type":"string"},{"const":"not_monitored","type":"string"}]},"latencyMs":{"description":"Latest probe round trip in ms (for `queue`: age of the oldest unprocessed job).","anyOf":[{"type":"number"},{"type":"null"}]},"uptime":{"description":"Uptime over the sampled minutes of the 90 days; null without samples.","anyOf":[{"type":"number"},{"type":"null"}]},"days":{"description":"Oldest first; the last entry is today (UTC).","type":"array","items":{"type":"object","required":["date","uptime","coverage","degradedMinutes","downMinutes","incidents"],"properties":{"date":{"description":"UTC day (`YYYY-MM-DD`).","type":"string"},"uptime":{"description":"Percent of the sampled minutes that were up (degraded counts as up), rounded down to 2 decimals; null when the day has no sample (\"no data\", never \"up\").","anyOf":[{"type":"number"},{"type":"null"}]},"coverage":{"description":"Share of the day (so far, for today) with a sample: 0–1. Below 1 the sampler was not running part of the day.","type":"number"},"degradedMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"downMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"incidents":{"description":"Incidents (automatic and manual) that overlap the day.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Incidents (automatic and manual) that overlap the day.","type":"integer"}]}}}}}}},"incidents":{"description":"Newest first, last 90 days, at most 50.","type":"array","items":{"type":"object","required":["id","source","components","severity","title","message","startedAt","resolvedAt"],"properties":{"id":{"type":"string"},"source":{"description":"`automatic`: derived from three consecutive failed samples (resolved after three good ones); `manual`: posted by an operator.","anyOf":[{"const":"automatic","type":"string"},{"const":"manual","type":"string"}]},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"severity":{"description":"`major` when the component was down, `minor` when degraded.","anyOf":[{"const":"none","type":"string"},{"const":"minor","type":"string"},{"const":"major","type":"string"}]},"title":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"message":{"description":"Manual incidents only (English).","anyOf":[{"type":"string"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"resolvedAt":{"description":"Null while the incident is open.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"maintenance":{"description":"Scheduled and in-progress windows, and those completed in the last 7 days.","type":"array","items":{"type":"object","required":["id","title","message","components","startsAt","endsAt","state"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"components":{"type":"array","items":{"description":"Component: `api` (REST API readiness), `realtime` (Pub/Sub fan-out behind the SSE streams), `queue` (reservation worker queue), `database` (PostgreSQL), `cache` (KeyDB / Redis), `assets` (map asset CDN), `dashboard` (web app and dashboard).","anyOf":[{"const":"api","type":"string"},{"const":"realtime","type":"string"},{"const":"queue","type":"string"},{"const":"database","type":"string"},{"const":"cache","type":"string"},{"const":"assets","type":"string"},{"const":"dashboard","type":"string"}]}},"startsAt":{"format":"date-time","type":"string"},"endsAt":{"description":"Null when open-ended.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"state":{"anyOf":[{"const":"scheduled","type":"string"},{"const":"in_progress","type":"string"},{"const":"completed","type":"string"}]}}}}},"required":["generatedAt","historyDays","sampleIntervalSeconds","lastSampleAt","stale","overall","components","incidents","maintenance"]}}}},"503":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"const":"redis_unavailable","type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"const":"redis_unavailable","type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"const":"redis_unavailable","type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiStatus","tags":["health"],"summary":"Public platform status and 90-day uptime","description":"Current state, latest latency and 90 days of daily uptime per component, automatic incidents and operator notices (incidents and maintenance). A sampler in the reservation worker probes every component every 30 s (STATUS_SAMPLE_INTERVAL_SEC) and keeps compact per-minute and per-day rollups in Redis (`status:*` keys, 90 days, bounded). Minutes without a sample — for example while the sampler itself is down — are reported as no data (`uptime: null`, `coverage` below 1), never as up. No authentication; IP rate limited; `Cache-Control: public, max-age=30, stale-while-revalidate=60`; gzip or br by `Accept-Encoding` with a weak `ETag` (304 on `If-None-Match`). No hostnames, error texts or other internals are exposed. 503 `redis_unavailable` when Redis cannot be read."}},"/api/auth/signup":{"post":{"parameters":[],"operationId":"postApiAuthSignup","tags":["auth"],"summary":"Create account and workspace","description":"Registers a new user, provisions a tenant workspace, and returns an HS256 dashboard JWT (`tenant_id`, `sub`, `email`, `role`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"workspaceName":{"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"workspaceName":{"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"},"workspaceName":{"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/login":{"post":{"parameters":[],"operationId":"postApiAuthLogin","tags":["auth"],"summary":"Sign in with email and password","description":"Returns tokens, or a second step: `{ twoFactorRequired: true, pendingToken }` when the account has two-factor authentication (finish with `POST /auth/2fa/verify`), or `{ twoFactorEnrollmentRequired: true, pendingToken }` when the workspace requires 2FA and the account has none yet (`POST /auth/2fa/enroll/start` then `/enroll/confirm`). No session exists until the second step succeeds.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"password":{"minLength":1,"maxLength":256,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/accept-invite":{"post":{"parameters":[],"operationId":"postApiAuthAccept-invite","tags":["auth"],"summary":"Accept a workspace invitation","description":"Creates a user in the invited tenant with the invited role and returns dashboard session tokens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","password"],"properties":{"token":{"minLength":32,"maxLength":128,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["token","password"],"properties":{"token":{"minLength":32,"maxLength":128,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["token","password"],"properties":{"token":{"minLength":32,"maxLength":128,"type":"string"},"password":{"minLength":8,"maxLength":256,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/invite/{token}":{"get":{"parameters":[{"schema":{"type":"string","minLength":32,"maxLength":128},"in":"path","name":"token","required":true}],"operationId":"getApiAuthInviteByToken","tags":["auth"],"summary":"Preview invitation metadata","responses":{"200":{}}}},"/api/auth/refresh":{"post":{"parameters":[],"operationId":"postApiAuthRefresh","tags":["auth"],"summary":"Rotate refresh token and issue new access JWT","description":"Consumes a one-time refresh token (Redis), revokes it, and returns a new refresh + access pair. Use `audience: viewer` for B2C embed tokens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"},"audience":{"anyOf":[{"const":"dashboard","type":"string"},{"const":"viewer","type":"string"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"},"audience":{"anyOf":[{"const":"dashboard","type":"string"},{"const":"viewer","type":"string"}]}}}},"text/plain":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"},"audience":{"anyOf":[{"const":"dashboard","type":"string"},{"const":"viewer","type":"string"}]}}}}}},"responses":{"200":{}}}},"/api/auth/logout":{"post":{"parameters":[],"operationId":"postApiAuthLogout","tags":["auth"],"summary":"Revoke refresh token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","properties":{"refreshToken":{"minLength":32,"maxLength":128,"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/auth/switch-workspace":{"post":{"parameters":[],"operationId":"postApiAuthSwitch-workspace","tags":["auth"],"summary":"Switch active workspace","description":"Re-issues dashboard JWT and refresh token for a workspace the user already belongs to. A workspace that requires two-factor authentication refuses members without it (403 `two_factor_enrollment_required`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tenantId"],"properties":{"tenantId":{"minLength":1,"maxLength":128,"type":"string"}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["tenantId"],"properties":{"tenantId":{"minLength":1,"maxLength":128,"type":"string"}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["tenantId"],"properties":{"tenantId":{"minLength":1,"maxLength":128,"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/auth/2fa/verify":{"post":{"parameters":[],"operationId":"postApiAuth2faVerify","tags":["auth"],"summary":"Complete sign-in with a two-factor code","description":"Second step after `POST /auth/login` answered `twoFactorRequired`. Send the 6-digit TOTP `code` or a one-time `recoveryCode`. Returns the same tokens as a password-only sign-in. A pending sign-in lives 5 minutes and allows 5 attempts; 10 failures lock second-factor checks for the account for 15 minutes (429 `too_many_attempts`). A code is accepted once (replay refused).","requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/2fa/enroll/start":{"post":{"parameters":[],"operationId":"postApiAuth2faEnrollStart","tags":["auth"],"summary":"Start the enrolment required by the workspace (during sign-in)","description":"When `POST /auth/login` answered `twoFactorEnrollmentRequired`, returns a new TOTP `secret` and its `otpauthUri` (render it as a QR code). Calling again replaces the secret.","requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/2fa/enroll/confirm":{"post":{"parameters":[],"operationId":"postApiAuth2faEnrollConfirm","tags":["auth"],"summary":"Confirm the required enrolment and finish signing in","description":"Verifies the first code of the new secret, enables 2FA, and returns the session tokens plus 10 one-time `recoveryCodes` (shown only now).","requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken","code"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken","code"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["pendingToken","code"],"properties":{"pendingToken":{"minLength":32,"maxLength":128,"description":"Pending sign-in token returned by `POST /auth/login`.","type":"string"},"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/me":{"get":{"operationId":"getApiAuthMe","tags":["auth"],"summary":"Current dashboard user","description":"Returns the authenticated user when the Bearer token was issued by `/auth/login` or `/auth/signup`.","responses":{"200":{}}}},"/api/auth/2fa":{"get":{"operationId":"getApiAuth2fa","tags":["auth"],"summary":"Two-factor status of the signed-in user","description":"`{ enabled, enabledAt, recoveryCodesRemaining, workspaceRequiresTwoFactor }`.","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/auth/2fa/setup":{"post":{"operationId":"postApiAuth2faSetup","tags":["auth"],"summary":"Start two-factor enrolment","description":"Returns a new TOTP `secret` and `otpauthUri` (render as a QR code client-side). Confirm within 10 minutes with `POST /auth/2fa/enable`.","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/auth/2fa/enable":{"post":{"parameters":[],"operationId":"postApiAuth2faEnable","tags":["auth"],"summary":"Confirm enrolment and enable two-factor authentication","description":"Verifies a code of the pending secret, enables 2FA and returns 10 one-time `recoveryCodes` (stored hashed; shown only now).","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["code"],"properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["code"],"properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["code"],"properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/2fa/disable":{"post":{"parameters":[],"operationId":"postApiAuth2faDisable","tags":["auth"],"summary":"Disable two-factor authentication","description":"Requires a current TOTP `code` or a `recoveryCode`. Refused (409) while the current workspace requires 2FA.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}}}},"responses":{"200":{}}}},"/api/auth/2fa/recovery-codes":{"post":{"parameters":[],"operationId":"postApiAuth2faRecovery-codes","tags":["auth"],"summary":"Replace the recovery codes","description":"Requires a current TOTP `code` (or a recovery code). Invalidates every previous recovery code and returns 10 new ones.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"code":{"minLength":6,"maxLength":12,"description":"6-digit code from the authenticator app.","type":"string"},"recoveryCode":{"minLength":10,"maxLength":24,"description":"One-time recovery code (`xxxxx-xxxxx`).","type":"string"}}}}}},"responses":{"200":{}}}},"/api/events":{"get":{"parameters":[{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"Only events created from this chart.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"chartId","required":false},{"description":"Inclusive lower bound on the event date (ISO 8601 date-time).","schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":false},{"description":"Inclusive upper bound on the event date (ISO 8601 date-time).","schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false},{"description":"Case-insensitive substring match on the event name.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"q","required":false},{"description":"Exact event key.","schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"key","required":false},{"description":"Events marked as ended (`isInThePast`): `include` (default), `exclude` or `only`.","schema":{"anyOf":[{"const":"include","type":"string"},{"const":"exclude","type":"string"},{"const":"only","type":"string"}]},"in":"query","name":"ended","required":false},{"description":"`date` (default): event date ascending. `created`: creation time, newest first.","schema":{"anyOf":[{"const":"date","type":"string"},{"const":"created","type":"string"}]},"in":"query","name":"sort","required":false}],"operationId":"getApiEvents","tags":["events"],"summary":"List events for the authenticated tenant","description":"Without query parameters, returns the legacy unpaginated `{ events }` shape (id, name, date, seat counter, map template, currency, isInThePast, createdAt). When any of `limit`, `cursor`, `chartId`, `from`, `to`, `q`, `key`, `ended` or `sort` is sent, returns a cursor page `{ items, nextCursor }` ordered by event date ascending, then id — or by creation time, newest first, with `sort=created` (stable across pages). Pass `nextCursor` back as `cursor` with the same filters until it is null. Paginated items include currency, isInThePast and createdAt and omit `mapTemplate` (no layout geometry is read).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiEvents","tags":["events"],"summary":"Create event","description":"Creates a new event scoped to the workspace. Optional `chartId` clones the published chart seat/GA templates into event inventory. A chart whose published map has error-level validation issues (workspace severity, see `GET /api/workspace/chart-validation`) is refused with 409 `chart_invalid` `{ chartId, issues: [{ code, severity, count, message }] }`; fix the chart in the designer and publish again. Optional `key` (unique per workspace, URL-safe, not UUID-shaped) can be used instead of the id in every event route; 409 `event_key_taken`. Creation-time configuration is applied **in the creation transaction** (all or nothing): `categories` / `objectCategories` (seats.io event categories: event-only categories and object assignments, validated and materialized exactly as on PATCH /events/{eventId}, applied first so the other settings see them; 400 `category_conflict` / `unknown_category`), `tableBookingConfig` and `categoryOverrides` (same semantics as PATCH), `channels` (inventory channels with their objects and GA `areaPlaces`; each `channelKey` secret is returned once) and `forSaleConfig` (`forSale: false` = listed objects/categories not for sale and `areaPlaces` withheld; `forSale: true` = only the listed ones for sale; without it, the workspace setting `newEventsNotForSale` starts every object off sale). Objects are seat labels/UUIDs or GA area names/UUIDs; unknown ones return 400 `objects_not_found`. GA areas sold as a whole (fixed or variable occupancy) go on or off sale whole: for-sale `areaPlaces` on them, or a channel quota below their capacity, return 400 `ga_area_whole` `{ areas: [{ areaId, label, capacity }] }`. Optional `seasonId` attaches the event afterwards and syncs NFS + channelKey from the season source. Optional `currency` (ISO 4217, two-decimal) sets the catalog currency used by quotes and the viewer. Emits the `event.created` webhook.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":1,"maxLength":64,"type":"string"},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":1,"maxLength":64,"type":"string"},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":1,"maxLength":64,"type":"string"},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/events/{eventId}":{"get":{"parameters":[{"description":"Event id (UUID) or event key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Comma-separated. `chartTableBooking` adds the table booking of the chart (`{ mode, tables? }` or null) — what `tableBookingConfig` overrides. `zones` adds `zones[]` (same items as GET /events/{eventId}/zones).","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z,]*$"},"in":"query","name":"expand","required":false}],"operationId":"getApiEventsByEventId","tags":["events"],"summary":"Retrieve one event","description":"Returns event metadata (key, name, date, chartId, currency, isInThePast, createdAt, `seasons` containing the event `[{ id, name }]`, map publication timestamps, per-event `categoryOverrides` and `tableBookingConfig` override, event-only `categories` and `objectCategories`) and Postgres inventory counters (`counts`). Counters reflect durable seat status only — live Redis holds are not included; use GET /api/events/:eventId/seats for real-time visual status. 404 when the event belongs to another tenant. `?expand=chartTableBooking` adds `chartTableBooking`: the table booking mode of the layout the event is drawn from (the published chart, or the event layout of a chart-less event) without the per-event override.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"deleteApiEventsByEventId","tags":["events"],"summary":"Delete event","description":"Deletes an event and its layout map data. `eventId` may be the event key. 409 when the event has commercial history. Emits the `event.deleted` webhook.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"patchApiEventsByEventId","tags":["events"],"summary":"Patch event: name, date, currency, overrides (isInThePast is irreversible)","description":"All fields optional. `date` is an ISO-8601 timestamp. `tableBookingConfig` overrides the chart table booking mode for this event only (`all_by_seat`, `all_by_table`, or `custom` with `tables: { tableId: by_seat | by_table }`); seat-to-table membership stays from the chart. `null` clears the override. Applied by reserve and in the published commercial layout. `categoryOverrides` **replaces** the per-event override map (keys = seat/GA `category` values; `null` clears all): `priceCents` sets the base price of every seat in the category (clearing it restores the chart price by seat label; ticket-type prices from the chart still take precedence), `tint` (#RRGGBB) recolors the category in this event's published geometry (republished atomically), `forSale: false` takes the category off sale (removing it puts it back on sale; retired seats stay off sale). `forSalePerCategory` (`{ category: boolean }`) toggles category availability without storing an override. Prices already accepted in orders never change. `key` sets, renames or clears (`null`) the event key (409 `event_key_taken`).\n\n**Chart switch** (`chartId`): moves the event to another published, non-archived chart of the workspace in **one transaction** with the event row and every seat/GA row locked in the commercial lock order. Seats are matched by `label`, GA areas by `name`: matched objects keep their ids (so holds, sales, orders and status history stay valid) and take the new geometry and category; sold/held/pending seats keep their price, other seats take the new chart price (or the category override). Unmatched objects without commercial state are removed, new chart objects are added (closed categories start not for sale), the event map draft is discarded and the geometry is republished atomically. Refused with **409 `chart_switch_conflict`** and nothing changed when a sold, held, pending or historical object has no counterpart, a GA area would have less capacity than sold + held + withheld places, or a GA area with sold or held places would change how it is sold (`occupancy`: regular / fixed / variable occupancy, or the capacity / minimum of a whole area): `objects: [{ objectId, label, objectType, reason: sold|held|pending|history|capacity|occupancy, required?, capacity? }]`. Other refusals: 404 (chart), 409 `chart_not_published`, `chart_archived`, `season_chart_mismatch`. Emits `event.updated` with `changed_fields`. A switch that removes objects of active listings is refused with 409 `listing_objects_removed` (`listings`: id, key, `removedObjects`) unless `withdrawAffectedListings: true` withdraws them in the same transaction (409 `listing_has_holds` while held); `chartSwitch.withdrawnListings` lists them.\n\n**Event categories** (seats.io event `categories` / `objectCategories`): `categories` **replaces** the event-only categories (`[{ key, label, color, accessible? }]`, max 128; keys and labels unique, and neither may reuse a chart category id or name: 400 `category_conflict`). They are added to this event's published layout categories (`{ id: key, name: label, tint, accessible }`), so the renderer, legend and filters show them. `objectCategories` **replaces** the object → category map (object label = seat label or GA area name; value = event category key, chart category id or chart category name; max 10000; unknown references: 400 `unknown_category` with `labels`). Assignments are resolved once, in this transaction: the objects' `category` becomes the category label (removing an assignment restores the previous category), reassigned seats take the price override of their new category (else the chart price) and close with a closed category, and the geometry is republished. Best available (`categories` filter), quotes, synced price tables (keyed by the label), sales rules, reports and the section aggregates of the renderer all see the event category. Events that use neither keep their geometry hash. Changes emit `event.categories_updated` (event log and admin webhook) and `event.updated` with `changed_fields` `categories` / `objectCategories`.\n\nResponse: `{ id, key, name, date, currency, isInThePast, chartId, categoryOverrides, tableBookingConfig, categories, objectCategories, forSaleUpdated?, chartSwitch?: { previousChartId, chartId, keptSeats, addedSeats, removedSeats, keptGaAreas, addedGaAreas, removedGaAreas } }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isInThePast":{"type":"boolean"},"name":{"minLength":1,"maxLength":512,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"tableBookingConfig":{"anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"forSalePerCategory":{"type":"object","patternProperties":{"^(.*)$":{"type":"boolean"}}},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the event key. 409 `event_key_taken` when another event of the workspace uses it.","anyOf":[{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"format":"uuid","description":"Switch the event to another published chart of the workspace (see description).","type":"string"},"withdrawAffectedListings":{"description":"Chart switch only: withdraw the active listings whose objects have no counterpart in the target chart, in the same transaction (409 `listing_has_holds` while any of their objects is held). Without it such a switch is refused with 409 `listing_objects_removed` (`listings`).","type":"boolean"},"categories":{"description":"Event-only categories (replaces the list; null clears).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name). Replaces the map; null clears.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"isInThePast":{"type":"boolean"},"name":{"minLength":1,"maxLength":512,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"tableBookingConfig":{"anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"forSalePerCategory":{"type":"object","patternProperties":{"^(.*)$":{"type":"boolean"}}},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the event key. 409 `event_key_taken` when another event of the workspace uses it.","anyOf":[{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"format":"uuid","description":"Switch the event to another published chart of the workspace (see description).","type":"string"},"withdrawAffectedListings":{"description":"Chart switch only: withdraw the active listings whose objects have no counterpart in the target chart, in the same transaction (409 `listing_has_holds` while any of their objects is held). Without it such a switch is refused with 409 `listing_objects_removed` (`listings`).","type":"boolean"},"categories":{"description":"Event-only categories (replaces the list; null clears).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name). Replaces the map; null clears.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]}}}},"text/plain":{"schema":{"type":"object","properties":{"isInThePast":{"type":"boolean"},"name":{"minLength":1,"maxLength":512,"type":"string"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"tableBookingConfig":{"anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"forSalePerCategory":{"type":"object","patternProperties":{"^(.*)$":{"type":"boolean"}}},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the event key. 409 `event_key_taken` when another event of the workspace uses it.","anyOf":[{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"format":"uuid","description":"Switch the event to another published chart of the workspace (see description).","type":"string"},"withdrawAffectedListings":{"description":"Chart switch only: withdraw the active listings whose objects have no counterpart in the target chart, in the same transaction (409 `listing_has_holds` while any of their objects is held). Without it such a switch is refused with 409 `listing_objects_removed` (`listings`).","type":"boolean"},"categories":{"description":"Event-only categories (replaces the list; null clears).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name). Replaces the map; null clears.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/status-changes":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"Exact action: durable `sell`, `cancel`, `booked`, `free`, `resale`, `retired`, a custom status, or Redis-only `hold`, `release`, `hold_expired`.","schema":{"type":"string","minLength":1,"maxLength":32},"in":"query","name":"action","required":false},{"description":"Only transitions of this order.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"orderId","required":false},{"description":"Seat label or GA area name to search (case-insensitive), see `labelMatch`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"label","required":false},{"description":"`exact` (default), `contains`, `prefix` (begins with) or `suffix` (ends with). Exact/prefix/suffix use an index.","schema":{"anyOf":[{"const":"exact","type":"string"},{"const":"contains","type":"string"},{"const":"prefix","type":"string"},{"const":"suffix","type":"string"}]},"in":"query","name":"labelMatch","required":false},{"description":"Only changes of this origin: `api_key`, `dashboard_user`, `buyer_session`, `system_expiry`, or `unknown` (recorded before origins existed).","schema":{"anyOf":[{"const":"api_key","type":"string"},{"const":"dashboard_user","type":"string"},{"const":"buyer_session","type":"string"},{"const":"system_expiry","type":"string"},{"const":"unknown","type":"string"}]},"in":"query","name":"origin","required":false},{"description":"`newest` (default) or `oldest` first. Keep the same sort while following `nextCursor`.","schema":{"anyOf":[{"const":"newest","type":"string"},{"const":"oldest","type":"string"}]},"in":"query","name":"sort","required":false},{"description":"Seat id (UUID) or exact seat label.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"objectId","required":false}],"operationId":"getApiEventsByEventIdStatus-changes","tags":["events"],"summary":"List status changes of an event","description":"Cursor-paginated status history: sales, cancellations, administrative status changes, holds, releases and hold expiries, with their origin. Items: `id`, `objectId`, `objectLabel`, `objectType` (`seat` | `generalAdmission`), `action`, `quantity`, `previousStatus` / `newStatus` (durable seat status), `previousObjectStatus` / `newObjectStatus` (object status vocabulary), `orderId`, `holdOwnerHash` (tenant-scoped digest of the hold session — the same value on a hold, its release or expiry; the raw holder identity is never returned), `ticketBuyerId`, `reason`, `origin` (`{ kind: api_key | dashboard_user | buyer_session | system_expiry, actorId, actorName, ip }`, null for changes recorded before origins existed; `ip` is truncated to /24 IPv4 or /48 IPv6) and `createdAt`. Durable changes come from inventory_transitions; holds, releases and hold expiries come from the status-history stream drained in batches by the worker (eventually consistent, typically within seconds). Pass `nextCursor` back as `cursor` until it is null. Requires content_write (admin, map editor) or a partner API key.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/objects/{objectId}/status-changes":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Seat id (UUID) or exact seat label.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"objectId","required":true},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"Exact action: durable `sell`, `cancel`, `booked`, `free`, `resale`, `retired`, a custom status, or Redis-only `hold`, `release`, `hold_expired`.","schema":{"type":"string","minLength":1,"maxLength":32},"in":"query","name":"action","required":false},{"description":"Only transitions of this order.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"orderId","required":false},{"description":"Seat label or GA area name to search (case-insensitive), see `labelMatch`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"label","required":false},{"description":"`exact` (default), `contains`, `prefix` (begins with) or `suffix` (ends with). Exact/prefix/suffix use an index.","schema":{"anyOf":[{"const":"exact","type":"string"},{"const":"contains","type":"string"},{"const":"prefix","type":"string"},{"const":"suffix","type":"string"}]},"in":"query","name":"labelMatch","required":false},{"description":"Only changes of this origin: `api_key`, `dashboard_user`, `buyer_session`, `system_expiry`, or `unknown` (recorded before origins existed).","schema":{"anyOf":[{"const":"api_key","type":"string"},{"const":"dashboard_user","type":"string"},{"const":"buyer_session","type":"string"},{"const":"system_expiry","type":"string"},{"const":"unknown","type":"string"}]},"in":"query","name":"origin","required":false},{"description":"`newest` (default) or `oldest` first. Keep the same sort while following `nextCursor`.","schema":{"anyOf":[{"const":"newest","type":"string"},{"const":"oldest","type":"string"}]},"in":"query","name":"sort","required":false}],"operationId":"getApiEventsByEventIdObjectsByObjectIdStatus-changes","tags":["events"],"summary":"List status changes of one object","description":"Same as GET /api/events/:eventId/status-changes, scoped to one seat. Items: `id`, `objectId`, `objectLabel`, `objectType` (`seat` | `generalAdmission`), `action`, `quantity`, `previousStatus` / `newStatus` (durable seat status), `previousObjectStatus` / `newObjectStatus` (object status vocabulary), `orderId`, `holdOwnerHash` (tenant-scoped digest of the hold session — the same value on a hold, its release or expiry; the raw holder identity is never returned), `ticketBuyerId`, `reason`, `origin` (`{ kind: api_key | dashboard_user | buyer_session | system_expiry, actorId, actorName, ip }`, null for changes recorded before origins existed; `ip` is truncated to /24 IPv4 or /48 IPv6) and `createdAt`. Durable changes come from inventory_transitions; holds, releases and hold expiries come from the status-history stream drained in batches by the worker (eventually consistent, typically within seconds). Pass `nextCursor` back as `cursor` until it is null. Requires content_write (admin, map editor) or a partner API key.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/objects/{objectId}":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Seat or GA area id (UUID), or its exact label / GA name.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"objectId","required":true}],"operationId":"getApiEventsByEventIdObjectsByObjectId","tags":["events"],"summary":"Retrieve one object (seat or GA area)","description":"Looks the object up by id, then by exact label (409 when the label is not unique). Returns label, category, price (cents), durable `dbStatus`, `visualStatus` (Postgres status merged with live Redis hold presence), `ticketBuyerId` of the current state, `forSale`, the object status overlay (`objectStatus`, `orderId`, `extraData`) and `section` when the seat lies inside a named section guide, and `zone` / `zoneId` (layout zone: explicit assignment, then the section zone, then the zone outline; null outside every zone), and `entrance` (the most specific of seat, row, table and section; GA areas use their own). Seats add `rowLabel`, `displayedLabel`, `accessibility` (stored form: the four original flags as booleans, newer flags in the `amenities` bitmask, `seatType`), `amenityFlags` (every set flag by name) and `seatType` (default chair). GA areas add capacity, soldCount, heldCount and availableCount; areas sold as a whole add the seats.io `areaType` (`fixedOccupancy` / `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy` (their `availableCount` is the capacity or 0: never part of the area). `channelKeyId` is returned to partner API keys and workspace admins only. Requires content_write.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/objects/actions/retrieve":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdObjectsActionsRetrieve","tags":["events"],"summary":"Retrieve several objects at once (seats.io retrieveObjectInfos)","description":"Same object info as GET /events/{eventId}/objects/{objectId} for up to 100 objects in one call: `{ objects: { [ref]: info }, notFound: [ref], ambiguous: [ref] }` (ambiguous = a label shared by several objects; use the id). Read-only (API keys need the `read` scope). Requires content_write for dashboard sessions.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":100,"description":"Seat or GA area ids, labels or GA names.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":100,"description":"Seat or GA area ids, labels or GA names.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":100,"description":"Seat or GA area ids, labels or GA names.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/objects":{"get":{"parameters":[{"description":"Event id (UUID) or event key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Opaque ticket buyer id (required).","schema":{"type":"string","maxLength":256},"in":"query","name":"ticketBuyerId","required":false}],"operationId":"getApiEventsByEventIdObjects","tags":["events"],"summary":"List the objects of a ticket buyer","description":"Every object of the event associated with `ticketBuyerId`: booked/resale/custom-status seats (object status overlays and active sales), booked general-admission places (remaining quantity per area, administrative releases deducted), and live holds created with that buyer id (`reservedByToken`; seat locks still owned by the hold, GA places still held). Hold tokens are never returned. Cancelled orders and released objects drop out. Requires content_write (partner API key or admin/map editor).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts":{"get":{"parameters":[{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"Case-insensitive substring match on the chart name, or the exact chart key.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"q","required":false},{"description":"`false` (default) hides archived charts, `true` returns only archived charts, `all` returns both.","schema":{"anyOf":[{"const":"false","type":"string"},{"const":"true","type":"string"},{"const":"all","type":"string"}]},"in":"query","name":"archived","required":false},{"description":"Comma-separated tags (case-insensitive, at most 5); a chart must carry all of them.","schema":{"type":"string","minLength":1,"maxLength":256},"in":"query","name":"tag","required":false},{"description":"`name` (default): name A→Z. `recent`: last draft save / publish first, then creation time.","schema":{"anyOf":[{"const":"name","type":"string"},{"const":"recent","type":"string"}]},"in":"query","name":"sort","required":false},{"description":"Comma-separated. `events` adds `events[]` (`{ id, key, name, date, isInThePast }`, soonest first, at most 100 per chart unless `eventsLimit` says otherwise; `eventCount` has the total). `venueType` adds `venueType` (`simple` | `sections` | `zones`: the designer template of the published chart, else of its draft). `zones` adds `zones[]` of the published chart, else of its draft (same items as GET /charts/{chartId}/zones; no seat geometry is read). `validation` is accepted for seats.io compatibility (`validation` is always part of paginated items).","schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"expand","required":false},{"description":"With `expand=events`: events listed per chart (1–1000, default 100).","schema":{"minimum":1,"maximum":1000,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":1000,"multipleOf":1,"description":"With `expand=events`: events listed per chart (1–1000, default 100).","type":"number"}]},"in":"query","name":"eventsLimit","required":false}],"operationId":"getApiCharts","tags":["charts"],"summary":"List charts","description":"Without query parameters, returns the legacy `{ charts }` shape (newest draft first, at most 200 rows, archived charts hidden); its items carry `publishedValidation` (stored counts of the published map, what event creation checks). When `limit`, `cursor`, `q`, `archived`, `tag`, `sort` or `expand` is sent, returns a cursor page `{ items, nextCursor }` (stable across pages, no row cap). Pass `nextCursor` back as `cursor` with the same filters until it is null. Paginated items add `eventCount`, `recentEvents` (up to 3 `{ id, name, date, status: upcoming | past | ended }`: upcoming soonest first, then past most recent first, then ended), `archivedAt`, `createdAt`, `tags`, `latestVersion`, `hasThumbnail` and `thumbnailUrl` (authenticated relative path, null until first publish) and omit `mapTemplate` (no layout geometry is read). `validation` `{ errors, warnings, issues: [{ code, severity, count, message }] }` holds the per-rule counts stored at the last draft save / publish under the current workspace severity (null when never computed); the rules are not re-run. Archived charts are hidden unless `archived=true|all`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiCharts","tags":["charts"],"summary":"Create chart","description":"Creates a reusable seating chart. Optional in the same call: `key` (partner key, unique per workspace, usable wherever a chart id is accepted; 409 `chart_key_taken`), `venueType` (`simple` | `sections` | `zones`, the designer template) and `categories` (up to 128 `{ key?, label, color, accessible? }`; keys and labels unique, case-insensitive). With `venueType` or `categories` an initial draft holding them is written in the same transaction (`hasDraft: true`). Author geometry via `PUT /api/charts/:chartId/map/draft` and publish before attaching to events. Besides a partner key or a dashboard session with `content_write`, a designer embed token minted without `chartId` and `eventId` may create charts (the embedded designer opened without a chart); other embed tokens get 403 `ERR_EMBED_SCOPE`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","description":"Partner key, unique in the workspace (409 `chart_key_taken`), accepted wherever a chart id is. Never UUID-shaped.","type":"string"},"venueType":{"description":"Designer template: `simple` (rows), `sections` (sections and floors) or `zones`. Default `simple`.","anyOf":[{"const":"simple","type":"string"},{"const":"sections","type":"string"},{"const":"zones","type":"string"}]},"categories":{"maxItems":128,"type":"array","items":{"type":"object","required":["label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable category id (generated when omitted).","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Category name; seats and GA areas reference it.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"accessible":{"type":"boolean"}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","description":"Partner key, unique in the workspace (409 `chart_key_taken`), accepted wherever a chart id is. Never UUID-shaped.","type":"string"},"venueType":{"description":"Designer template: `simple` (rows), `sections` (sections and floors) or `zones`. Default `simple`.","anyOf":[{"const":"simple","type":"string"},{"const":"sections","type":"string"},{"const":"zones","type":"string"}]},"categories":{"maxItems":128,"type":"array","items":{"type":"object","required":["label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable category id (generated when omitted).","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Category name; seats and GA areas reference it.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"accessible":{"type":"boolean"}}}}}}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","description":"Partner key, unique in the workspace (409 `chart_key_taken`), accepted wherever a chart id is. Never UUID-shaped.","type":"string"},"venueType":{"description":"Designer template: `simple` (rows), `sections` (sections and floors) or `zones`. Default `simple`.","anyOf":[{"const":"simple","type":"string"},{"const":"sections","type":"string"},{"const":"zones","type":"string"}]},"categories":{"maxItems":128,"type":"array","items":{"type":"object","required":["label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable category id (generated when omitted).","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Category name; seats and GA areas reference it.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"accessible":{"type":"boolean"}}}}}}}}},"responses":{"200":{}}}},"/api/charts/tags":{"get":{"parameters":[{"description":"`false` (default) hides archived charts, `true` returns only archived charts, `all` returns both.","schema":{"anyOf":[{"const":"false","type":"string"},{"const":"true","type":"string"},{"const":"all","type":"string"}]},"in":"query","name":"archived","required":false}],"operationId":"getApiChartsTags","tags":["charts"],"summary":"List chart tags","description":"Every tag used by the tenant's charts with the number of charts carrying it, sorted case-insensitively (at most 500). Tags are case-insensitive: `VIP` and `vip` are the same tag; the returned spelling is the lexicographically smallest stored variant. Counts respect the `archived` filter (archived charts are excluded by default).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/tags":{"put":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"putApiChartsByChartIdTags","tags":["charts"],"summary":"Replace chart tags","description":"Replaces every tag on the chart. Returns the stored tags sorted case-insensitively. `422 ERR_CHART_TAG_INVALID` for an empty / too long / comma-containing tag, `422 ERR_CHART_TAG_LIMIT` above 20 distinct tags. Filter lists with `GET /api/charts?tag=vip,arena`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tags"],"properties":{"tags":{"maxItems":50,"description":"Full replacement set. Each tag is trimmed, inner whitespace collapsed and NFC-normalized; 1-40 characters, no commas or control characters. Duplicates are merged case-insensitively (first spelling wins). At most 20 distinct tags. Send `[]` to clear.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["tags"],"properties":{"tags":{"maxItems":50,"description":"Full replacement set. Each tag is trimmed, inner whitespace collapsed and NFC-normalized; 1-40 characters, no commas or control characters. Duplicates are merged case-insensitively (first spelling wins). At most 20 distinct tags. Send `[]` to clear.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["tags"],"properties":{"tags":{"maxItems":50,"description":"Full replacement set. Each tag is trimmed, inner whitespace collapsed and NFC-normalized; 1-40 characters, no commas or control characters. Duplicates are merged case-insensitively (first spelling wins). At most 20 distinct tags. Send `[]` to clear.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/preview-link":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"postApiChartsByChartIdPreview-link","tags":["charts"],"summary":"Create a public preview link","description":"Signed, expiring link to a read-only rendering of the chart's **published** geometry (no login, no event data, no prices — category names and colors only). Response `{ chartId, exp, signature, expiresAt, path }`; `path` is relative to the dashboard origin (`/preview/charts/{chartId}?exp=…&sig=…`). The signature is an HMAC with a per-chart secret; every link of a chart stays valid until it expires or DELETE revokes them all. 409 `chart_not_published` when the chart was never published.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresInDays":{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","type":"integer"}]}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"expiresInDays":{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","type":"integer"}]}}}},"text/plain":{"schema":{"type":"object","properties":{"expiresInDays":{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":365,"description":"Link lifetime in days (1–365, default 30).","type":"integer"}]}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"deleteApiChartsByChartIdPreview-link","tags":["charts"],"summary":"Revoke every public preview link of a chart","description":"Rotates the chart's preview secret: all links issued before stop working (within ~15 s on every API instance).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/zones":{"get":{"parameters":[{"description":"Chart id (UUID) or chart key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false}],"operationId":"getApiChartsByChartIdZones","tags":["charts"],"summary":"List the zones of a chart","description":"Zones (named areas above sections) of the **published** chart by default, in drawing order: `{ id, name, color, floorId, focalPoint, bounds, points }`. `focalPoint` is the zone's Best Available ranking origin (null: the chart focal point applies). `?version=draft` reads the unpublished designer draft instead (404 `draft_not_found` when there is none), so the published chart stays readable while a draft exists. Only the zone shapes are read (projected in PostgreSQL). Object counts per zone: `GET /charts/{chartId}/reports/by-zone.json`. Requires read access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/zones":{"get":{"parameters":[{"description":"Event id (UUID) or event key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdZones","tags":["events"],"summary":"List the zones of an event","description":"Zones of the layout the event is drawn from — its own published map when it has one, else the published chart — in drawing order, same item shape as `GET /charts/{chartId}/zones`. Only the zone shapes are read. Per-zone availability: `GET /events/{eventId}/reports/by-zone.json`. Requires read access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/map":{"get":{"parameters":[{"description":"Chart id (UUID) or chart key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false}],"operationId":"getApiChartsByChartIdMap","tags":["charts"],"summary":"Read one version of a chart map","description":"Returns the **published** chart map by default (`seats`, `shapes`, `referenceImage`, `backgroundImage`, `meta`), even while an unpublished draft exists (`meta.hasUnpublishedDraft` tells). `?version=draft` returns the draft only (404 `draft_not_found` without one; requires map authoring access, like `GET /charts/{chartId}/map/draft`, which falls back to the published map). 404 `not_published` for a chart that was never published. Validate a version with `POST /charts/{chartId}/actions/validate?version=published|draft`. Requires read access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}":{"get":{"parameters":[{"description":"Chart id (UUID) or chart key.","schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Comma-separated. `zones` adds `zones[]` (same items as GET /charts/{chartId}/zones) of the version selected by `version`. `events` adds `events[]` (the 10,000 most recently created events, newest first: `{ id, key, name, date, isInThePast, createdAt }`) and `numTotalEvents` (all events of the chart).","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z,]*$"},"in":"query","name":"expand","required":false},{"description":"`published` or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false}],"operationId":"getApiChartsByChartId","tags":["charts"],"summary":"Retrieve one chart","description":"Returns chart `key`, name and publication metadata: `publishedAt`, `draftUpdatedAt`, `isPublished`, `hasUnpublishedDraft`, `hasDraft`, published seat / GA template counts and the number of events using it, plus `archivedAt`, `createdAt`, `tags`, `latestVersion`, `hasThumbnail` and `thumbnailUrl`. Archived charts are still returned. Geometry blobs are never returned — use the map / draft endpoints. `?expand=zones` adds the zones of the published chart (`&version=draft`: of the draft; empty when that version does not exist). `?expand=events` adds the 10,000 most recently created events of the chart (`events`, newest first) and `numTotalEvents` (seats.io retrieveWithEvents). The path accepts the chart key. 404 when the chart belongs to another tenant.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"description":"Chart id (UUID) or chart key.","schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"operationId":"patchApiChartsByChartId","tags":["charts"],"summary":"Update chart name or key","description":"Updates the display name and/or the partner `key` of a reusable seating chart (at least one of them). Keys are unique per workspace, URL-safe and never UUID-shaped; after a rename the old key stops resolving at once on every API instance (Redis invalidation message; 5 s at most if it is lost).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the chart key. 409 `chart_key_taken` when another chart of the workspace uses it.","anyOf":[{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","type":"string"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the chart key. 409 `chart_key_taken` when another chart of the workspace uses it.","anyOf":[{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","type":"string"},{"type":"null"}]}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set, rename (`\"new-key\"`) or clear (`null`) the chart key. 409 `chart_key_taken` when another chart of the workspace uses it.","anyOf":[{"minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","type":"string"},{"type":"null"}]}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"operationId":"deleteApiChartsByChartId","tags":["charts"],"summary":"Delete chart permanently","description":"Permanently deletes a chart with its layout, draft, templates, tags and version history. Only archived charts that no event references can be deleted: `409 ERR_CHART_NOT_ARCHIVED` — archive it first (`POST /api/charts/:chartId/actions/archive`); `409 ERR_CHART_HAS_EVENTS` (`params.eventCount`) — keep it archived (events keep working) or delete the events. Requires the `content_delete` permission (dashboard admin / map editor; not available to partner API keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/actions/archive":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}},"text/plain":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}}}},"403":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"404":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}}},"operationId":"postApiChartsByChartIdActionsArchive","tags":["charts"],"summary":"Archive chart","description":"Soft-deletes a chart: it is hidden from `GET /api/charts` and `GET /api/charts/tags` unless `archived=true|all` is sent. Events created from the chart keep serving their maps and inventory unchanged, and the chart can still be opened, edited and published by id. Idempotent — archiving an archived chart keeps its original `archivedAt`. Permanent deletion (`DELETE /api/charts/:chartId`) is only allowed for archived charts without events.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/charts/{chartId}/actions/unarchive":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}},"text/plain":{"schema":{"type":"object","properties":{"chart":{"type":"object","required":["id","name","archivedAt"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"archivedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}}}},"required":["chart"]}}}},"403":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"404":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}}},"operationId":"postApiChartsByChartIdActionsUnarchive","tags":["charts"],"summary":"Unarchive chart","description":"Returns an archived chart to the default chart lists. Idempotent for charts that are not archived.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/charts/{chartId}/versions":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","note","hasThumbnail","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before version history existed (recorded on the first publish after upgrade).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"hasThumbnail":{"description":"True when the designer uploaded a rendered thumbnail; otherwise the thumbnail URL serves a placeholder.","type":"boolean"},"thumbnailUrl":{"description":"Relative API path (authenticated) of the immutable thumbnail for this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","note","hasThumbnail","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before version history existed (recorded on the first publish after upgrade).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"hasThumbnail":{"description":"True when the designer uploaded a rendered thumbnail; otherwise the thumbnail URL serves a placeholder.","type":"boolean"},"thumbnailUrl":{"description":"Relative API path (authenticated) of the immutable thumbnail for this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}},"text/plain":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","note","hasThumbnail","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before version history existed (recorded on the first publish after upgrade).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"hasThumbnail":{"description":"True when the designer uploaded a rendered thumbnail; otherwise the thumbnail URL serves a placeholder.","type":"boolean"},"thumbnailUrl":{"description":"Relative API path (authenticated) of the immutable thumbnail for this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}}}},"400":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"403":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"404":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}}},"operationId":"getApiChartsByChartIdVersions","tags":["charts"],"summary":"List published versions","description":"Immutable publish history, newest first, cursor-paginated (`limit` 1..100, default 20; pass `nextCursor` back as `cursor`). Every successful `POST /api/charts/:chartId/map/publish` appends one version. Snapshots are content-addressed: republishing or restoring identical content reuses the stored geometry (same `geometryHash`) instead of duplicating it. Charts published before history existed get a `baseline` version 1 on their next publish.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/charts/{chartId}/versions/{version}/actions/restore-to-draft":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Version number (1-based).","schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"description":"Version number (1-based).","type":"number"}]},"in":"path","name":"version","required":true}],"operationId":"postApiChartsByChartIdVersionsByVersionActionsRestore-to-draft","tags":["charts"],"summary":"Restore a version into the draft","description":"Overwrites the chart draft with the content of a published version (seats, shapes, categories, images). It never publishes: the live map and linked events are untouched until `POST /api/charts/:chartId/map/publish`, which runs the normal validation and records a new version. Any unpublished draft changes are replaced. Requires map authoring rights (admin / map editor dashboard role or `x-api-key`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/thumbnail":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Version number (1-based).","schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"description":"Version number (1-based).","type":"number"}]},"in":"query","name":"version","required":false}],"operationId":"getApiChartsByChartIdThumbnail","tags":["charts"],"summary":"Chart thumbnail","description":"Preview image of a published version: the WebP/PNG the designer rendered at publish time, or a server-side placeholder (PNG: zone / section / GA outlines, decorative shapes and category-coloured seat dots; first floor of multi-floor maps) until a render is attached. `X-Thumbnail-Source` says which. With `?version=N` a designer thumbnail is served `Cache-Control: private, max-age=31536000, immutable`; placeholders are cached for 5 minutes. Without `version` the latest published state is returned with `no-cache` and an `ETag` (send `If-None-Match` for `304`). 404 when the chart was never published.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/versions/{version}/thumbnail":{"put":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Version number (1-based).","schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"description":"Version number (1-based).","type":"number"}]},"in":"path","name":"version","required":true}],"operationId":"putApiChartsByChartIdVersionsByVersionThumbnail","tags":["charts"],"summary":"Attach a rendered thumbnail to a version","description":"Stores the preview the designer renders at publish time (offscreen render of the stage, ~640px, WebP). Written through the map-asset storage (`MAP_ASSET_STORAGE_DIR` / R2) when configured, otherwise kept inline. Set once: `409 ERR_THUMBNAIL_EXISTS` when the version already has one (version thumbnails are immutable). A version still showing the server placeholder (published through the API, or a baseline) accepts a render at any time — the designer attaches one when it shows that published map. For a chart published before version history, attaching to version 1 first records its published state as the baseline version. Magic bytes must match the declared type (`400 ERR_THUMBNAIL_INVALID`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["image"],"properties":{"image":{"minLength":32,"maxLength":699115,"pattern":"^data:image/(webp|png);base64,","description":"Data URL (`data:image/webp;base64,...` preferred, PNG accepted) of at most 512 KB decoded, ~640px wide.","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["image"],"properties":{"image":{"minLength":32,"maxLength":699115,"pattern":"^data:image/(webp|png);base64,","description":"Data URL (`data:image/webp;base64,...` preferred, PNG accepted) of at most 512 KB decoded, ~640px wide.","type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["image"],"properties":{"image":{"minLength":32,"maxLength":699115,"pattern":"^data:image/(webp|png);base64,","description":"Data URL (`data:image/webp;base64,...` preferred, PNG accepted) of at most 512 KB decoded, ~640px wide.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/actions/validate":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` or `draft`. Omitted: the draft when there is one, else the published map.","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false}],"operationId":"postApiChartsByChartIdActionsValidate","tags":["charts"],"summary":"Validate a chart map (dry run)","description":"Runs the exact validation that `POST /charts/{chartId}/map/publish` applies (payload limits, the designer rules at the workspace severity — see `GET /api/workspace/chart-validation` — and seat-id ownership) **without writing anything**. Without a body the current draft (or, when there is none, the published map) is validated; `?version=published` validates the published chart even while a draft exists and `?version=draft` only the draft (404 `not_published` / `draft_not_found` when that version does not exist). With a body `{ seats, shapes?, background?, reference? }` (same shape as PUT …/map/draft) that content is validated instead (`version` is ignored). Always 200 with `publishable`, counts and `issues[]` (`{ code, severity, objectIds, message, count }`). Requires read access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}}}},"text/plain":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/categories":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` or `draft`. Omitted: the draft when there is one, else the published map.","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false}],"operationId":"getApiChartsByChartIdCategories","tags":["charts"],"summary":"List the categories of a chart","description":"Categories (`{ id, name, color, ticketTypes, seatCount, gaAreaCount }`) of the draft, else the published map (`source` tells which). `?version=published` reads the published chart even while a draft exists; `?version=draft` reads only the draft (404 `draft_not_found` / `not_published` when that version does not exist). Requires read access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"postApiChartsByChartIdCategories","tags":["charts"],"summary":"Add a category to the chart draft","description":"Adds a category (`name`, optional `id`, `color` and `ticketTypes`) to the chart draft (created from the published map when there is none) and answers 201 `{ category }`. Names are unique per chart (case-insensitive): 409 `category_exists`. Publish the draft to make the category live. Requires chart write access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable id (generated when omitted).","type":"string"},"name":{"minLength":1,"maxLength":64,"description":"Seats and GA areas reference categories by name.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable id (generated when omitted).","type":"string"},"name":{"minLength":1,"maxLength":64,"description":"Seats and GA areas reference categories by name.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Stable id (generated when omitted).","type":"string"},"name":{"minLength":1,"maxLength":64,"description":"Seats and GA areas reference categories by name.","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/categories/{categoryId}":{"patch":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"categoryId","required":true}],"operationId":"patchApiChartsByChartIdCategoriesByCategoryId","tags":["charts"],"summary":"Update a category in the chart draft","description":"Renames a draft category, changes its `color` or replaces its `ticketTypes`. A rename relabels every draft seat and GA area of the category (`renamedSeats`, `renamedGaAreas`). 404 `not_found` when the category is not in the draft, 409 `category_exists` when another category uses the name. Requires chart write access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":64,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":64,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":64,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"Category colour `#RRGGBB`.","type":"string"},"ticketTypes":{"maxItems":32,"description":"Ticket types (tiers) sold in this category.","type":"array","items":{"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"type":"string"},"name":{"minLength":1,"maxLength":48,"type":"string"},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"primary":{"type":"boolean"},"unavailable":{"type":"boolean"},"unavailableReason":{"maxLength":80,"type":"string"},"originalPriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"categoryId","required":true},{"description":"Category id that receives the seats/GA areas of the deleted category.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"reassignTo","required":false}],"operationId":"deleteApiChartsByChartIdCategoriesByCategoryId","tags":["charts"],"summary":"Delete a category from the chart draft","description":"Removes a category from the chart draft. When draft seats or GA areas still use it, pass `?reassignTo=<categoryId>` to move them to another category first (`reassignedSeats`, `reassignedGaAreas`); without it the call answers 409 `category_in_use` with the counts. 404 `not_found` when the category is not in the draft. Requires chart write access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/versions/{version}/actions/copy":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"type":"number"}]},"in":"path","name":"version","required":true}],"operationId":"postApiChartsByChartIdVersionsByVersionActionsCopy","tags":["charts"],"summary":"Copy a chart version into a new chart","description":"Creates a new chart in the same workspace from one published version of this chart (see `GET /charts/{chartId}/versions`), with the chart tags. `name` defaults to \"<chart name> (v<version> copy)\". Answers `{ chart: { id, name, sourceChartId, sourceVersion } }`; 404 `ERR_CHART_VERSION_NOT_FOUND` for an unknown version. Counts against the plan chart limit. Requires chart write access.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/actions/copy-to-workspace":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"postApiChartsByChartIdActionsCopy-to-workspace","tags":["charts"],"summary":"Copy a chart to another workspace","description":"Copies the chart (its draft, else the published map) into another workspace as a new **unpublished** chart with fresh object ids. Callers: a dashboard session, or a **workspace admin API key** (`POST /api/keys` with `workspaceAdmin: true`; a scoped key also needs `charts_write`) — other API keys get 403 `ERR_PARTNER_SCOPE`. The user (for a key: the admin it is bound to, re-checked on every call) must be an **admin of both workspaces** (403 `ERR_FORBIDDEN_ROLE`). The target must be active (409 `workspace_deactivated`) and within its plan's chart limit. Both workspaces get a `chart.copy_to_workspace` audit entry; the target also gets `chart.created`. Same workspace: use `POST /charts/{chartId}/duplicate` (400).\n\n**From another workspace** (seats.io `copyFromWorkspaceTo`): `fromWorkspaceId` names the source workspace, so one company admin key copies a chart from workspace X to workspace Y without a key of X. The admin must administer both X and Y (403 `ERR_FORBIDDEN_ROLE`, also when X is not one of its workspaces); 404 `ERR_CHART_NOT_FOUND` when the chart is not in X. X and Y get `chart.copy_to_workspace` and, when the calling workspace is neither, it gets `chart.copy_between_workspaces`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workspaceId"],"properties":{"workspaceId":{"minLength":1,"maxLength":128,"description":"Target workspace id (see GET /workspaces).","type":"string"},"fromWorkspaceId":{"minLength":1,"maxLength":128,"description":"Source workspace id (default: the calling workspace). seats.io copyFromWorkspaceTo: copy a chart of another workspace the same admin administers; pass the chart id (UUID), keys resolve in the calling workspace only.","type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["workspaceId"],"properties":{"workspaceId":{"minLength":1,"maxLength":128,"description":"Target workspace id (see GET /workspaces).","type":"string"},"fromWorkspaceId":{"minLength":1,"maxLength":128,"description":"Source workspace id (default: the calling workspace). seats.io copyFromWorkspaceTo: copy a chart of another workspace the same admin administers; pass the chart id (UUID), keys resolve in the calling workspace only.","type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["workspaceId"],"properties":{"workspaceId":{"minLength":1,"maxLength":128,"description":"Target workspace id (see GET /workspaces).","type":"string"},"fromWorkspaceId":{"minLength":1,"maxLength":128,"description":"Source workspace id (default: the calling workspace). seats.io copyFromWorkspaceTo: copy a chart of another workspace the same admin administers; pass the chart id (UUID), keys resolve in the calling workspace only.","type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/duplicate":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"operationId":"postApiChartsByChartIdDuplicate","tags":["charts"],"summary":"Duplicate chart","description":"Creates a new unpublished chart draft copied from the source chart (seats, GA areas, shapes, images).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/map/draft":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"operationId":"getApiChartsByChartIdMapDraft","tags":["charts"],"summary":"Load chart map draft for designer","description":"The draft (else the published map) the designer edits, plus `draftChartDrawingsEnabled` (workspace setting, default true; false: the designer publishes every save directly, see `GET /api/workspace/defaults`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"put":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"operationId":"putApiChartsByChartIdMapDraft","tags":["charts"],"summary":"Save chart map draft","description":"Saves the designer draft (`draftUpdatedAt` in the response). With `baseDraftUpdatedAt` the save is conflict-safe: 409 `draft_conflict` (with the current `draftUpdatedAt`) when the draft changed since that version — used by the designer autosave.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/charts/{chartId}/map/publish":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"chartId","required":true}],"responses":{"422":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}}}}},"operationId":"postApiChartsByChartIdMapPublish","tags":["charts"],"summary":"Publish chart map template","description":"Promotes draft → `chart_seat_templates` + `chart_layouts`. Events clone templates on create.\n\nThe draft is validated with the same rules the designer runs, at the **workspace severity** (`GET /api/workspace/chart-validation`; rules set to `off` are skipped). Error-level issues return `422 { error: \"validation_failed\", issues: [{ code, severity, objectIds, message, count, scope? }] }` (warnings included); warning-level issues do not block and are returned in `warnings`. Per-rule counts are stored with the version, so `GET /api/charts` (`validation`) and event creation (409 `chart_invalid`) never re-run the rules.\n\nEvery successful publish appends an immutable version (`version`, `geometryHash`; `sameAsVersion` names an earlier version with identical content, e.g. after a restore). An optional JSON body `{ \"note\": \"...\" }` (≤ 280 chars) is stored on the version. See `GET /api/charts/:chartId/versions`. Emits the `chart.published` webhook. Use `POST /api/charts/:chartId/actions/validate` for a dry run.\n\nWith `{ \"autoPublish\": true }` (designer auto-publish, off by default) the publish only runs while no event uses the chart; otherwise 409 `{ code: \"ERR_AUTO_PUBLISH_CHART_HAS_EVENTS\", params: { eventCount } }`. Workspaces that publish designer changes directly (`draftChartDrawingsEnabled: false`) skip that check: every designer save is published.\n\nGA areas sold as a whole: a change of how an area is sold (regular / `fixedOccupancy` / `variableOccupancy`, and the capacity, `minOccupancy` or per-area pricing of a whole area) also reaches the events of the chart. It is refused with 409 `{ error: \"ga_area_occupancy_locked\", objects: [{ eventId, objectId, label, soldCount, heldCount }] }` while any of those event areas has sold or held places (nothing is published).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"maxLength":280,"description":"Optional release note stored on the version record (shown in version history).","type":"string"},"autoPublish":{"description":"Set by the designer auto-publish option. The publish is refused with 409 `ERR_AUTO_PUBLISH_CHART_HAS_EVENTS` (params.eventCount) when any event uses the chart, unless the workspace publishes designer changes directly (`draftChartDrawingsEnabled: false`, see GET /api/workspace/defaults).","type":"boolean"}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","properties":{"note":{"maxLength":280,"description":"Optional release note stored on the version record (shown in version history).","type":"string"},"autoPublish":{"description":"Set by the designer auto-publish option. The publish is refused with 409 `ERR_AUTO_PUBLISH_CHART_HAS_EVENTS` (params.eventCount) when any event uses the chart, unless the workspace publishes designer changes directly (`draftChartDrawingsEnabled: false`, see GET /api/workspace/defaults).","type":"boolean"}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","properties":{"note":{"maxLength":280,"description":"Optional release note stored on the version record (shown in version history).","type":"string"},"autoPublish":{"description":"Set by the designer auto-publish option. The publish is refused with 409 `ERR_AUTO_PUBLISH_CHART_HAS_EVENTS` (params.eventCount) when any event uses the chart, unless the workspace publishes designer changes directly (`draftChartDrawingsEnabled: false`, see GET /api/workspace/defaults).","type":"boolean"}},"additionalProperties":false}}}}}},"/api/charts/{chartId}/map/draft/actions/discard":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true}],"operationId":"postApiChartsByChartIdMapDraftActionsDiscard","tags":["charts"],"summary":"Discard chart draft","description":"Throws away unpublished draft changes; `GET .../map/draft` then returns the published map again. The published map, versions and linked events are untouched. `409 ERR_DRAFT_NOTHING_TO_DISCARD` when the draft has no unpublished changes, `409 ERR_MAP_NEVER_PUBLISHED` when the chart was never published (there is no version to reset to).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/duplicate":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdDuplicate","tags":["events"],"summary":"Duplicate event","description":"Creates a new event with copied seat and GA inventory (all available), including inventory_channels (same secrets) and remapped channel_key_id + for_sale. Reservations are not copied.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"},"date":{"maxLength":64,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"},"date":{"maxLength":64,"type":"string"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"maxLength":512,"type":"string"},"date":{"maxLength":64,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-object-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-object-status.json","tags":["events"],"summary":"Detailed report grouped by object status","description":"seats.io detailed byStatus: buckets are the object statuses (`available`, `locked`, `sold`, a custom object status, `resale`, …), each with its objects. `GET /events/{eventId}/reports/by-status.json` keeps returning counts only. Optional `?status=` returns only that status (seats.io `byStatus(event, status)`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-object-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-object-status.csv","tags":["events"],"summary":"Detailed report grouped by object status (CSV)","description":"seats.io detailed byStatus: buckets are the object statuses (`available`, `locked`, `sold`, a custom object status, `resale`, …), each with its objects. `GET /events/{eventId}/reports/by-status.json` keeps returning counts only. Optional `?status=` returns only that status (seats.io `byStatus(event, status)`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-availability.json","tags":["events"],"summary":"Detailed report grouped by availability (available / not_available)","description":"Buckets `available` and `not_available`; each row carries `availabilityReason`. Optional `?availability=`. GA areas with at least one free place are `available`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-availability.csv","tags":["events"],"summary":"Detailed report grouped by availability (available / not_available) (CSV)","description":"Buckets `available` and `not_available`; each row carries `availabilityReason`. Optional `?availability=`. GA areas with at least one free place are `available`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-availability-reason.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this reason bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"reason","required":false},{"description":"Same as `reason`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availabilityReason","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-availability-reason.json","tags":["events"],"summary":"Detailed report grouped by availability reason","description":"Buckets available, resale, booked, held, custom_status, not_for_sale. GA areas use their primary reason (available when any place is free). Optional `?reason=` (or `?availabilityReason=`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-availability-reason.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this reason bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"reason","required":false},{"description":"Same as `reason`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availabilityReason","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-availability-reason.csv","tags":["events"],"summary":"Detailed report grouped by availability reason (CSV)","description":"Buckets available, resale, booked, held, custom_status, not_for_sale. GA areas use their primary reason (available when any place is free). Optional `?reason=` (or `?availabilityReason=`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-category-key.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this categoryKey bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryKey","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-category-key.json","tags":["events"],"summary":"Detailed report grouped by category key","description":"Seats.io byCategoryKey: buckets are the category keys (chart category id, or the event category key of an event-only category); a category value no category defines is its own key; objects without a category → NO_CATEGORY. Rows carry `categoryKey` next to `category` (the label). Optional `?categoryKey=`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-category-key.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this categoryKey bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryKey","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-category-key.csv","tags":["events"],"summary":"Detailed report grouped by category key (CSV)","description":"Seats.io byCategoryKey: buckets are the category keys (chart category id, or the event category key of an event-only category); a category value no category defines is its own key; objects without a category → NO_CATEGORY. Rows carry `categoryKey` next to `category` (the label). Optional `?categoryKey=`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-category-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this categoryLabel bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryLabel","required":false},{"description":"Same as `categoryLabel`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-category-label.json","tags":["events"],"summary":"Detailed report grouped by category label","description":"Seats.io byCategoryLabel: buckets are the category labels (the objects' `category` value, as in the legacy by-category report); objects without a category → NO_CATEGORY. Optional `?categoryLabel=` (or `?category=`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-category-label.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this categoryLabel bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryLabel","required":false},{"description":"Same as `categoryLabel`.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-category-label.csv","tags":["events"],"summary":"Detailed report grouped by category label (CSV)","description":"Seats.io byCategoryLabel: buckets are the category labels (the objects' `category` value, as in the legacy by-category report); objects without a category → NO_CATEGORY. Optional `?categoryLabel=` (or `?category=`). Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-zone.json","tags":["events"],"summary":"Detailed report grouped by zone","description":"Zones are `zone` layout shapes. Seats resolve explicit assignment (layoutMeta.seatZones) → their section's zone → the zone outline containing them; GA areas use `zoneId` or their centre. Others → NO_ZONE. Optional `?zone=` (zone name). The response lists the layout `zones`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsBy-zone.csv","tags":["events"],"summary":"Detailed report grouped by zone (CSV)","description":"Zones are `zone` layout shapes. Seats resolve explicit assignment (layoutMeta.seatZones) → their section's zone → the zone outline containing them; GA areas use `zoneId` or their centre. Others → NO_ZONE. Optional `?zone=` (zone name). The response lists the layout `zones`. Rows: `objectId`, `label`, `objectType` (seat|ga), `status` (durable status or object status overlay), `category`, `section`, `zone`, `forSale`, `channelKeyId`, `orderId`, `availability` (available|not_available) and `availabilityReason` (available|resale|booked|held|custom_status|not_for_sale). GA rows add `capacity`, `soldCount`, `heldCount`, `availableCount` and `places` (places per reason). Availability: custom object status → custom_status; sold → booked (resale overlay → resale, still available); durable or live hold → held; not for sale → not_for_sale. `liveHolds` tells whether active buyer holds were merged (false when skipped or Redis is unavailable). GA areas sold as a whole add `areaType` (`fixedOccupancy` | `variableOccupancy`), `bookAsAWhole: true` and, for variable occupancy, `minOccupancy` / `maxOccupancy`; their places are available all together or not at all (a partly withheld whole area is not_for_sale). The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-status.json","tags":["events"],"summary":"Summary report by status","description":"Counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-status.csv","tags":["events"],"summary":"Summary report by status (CSV)","description":"Counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-status.json","tags":["events"],"summary":"Deep summary report by status","description":"seats.io deep summary by status: the buckets of the summary by status, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category.json","tags":["events"],"summary":"Summary report by category","description":"Counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category.csv","tags":["events"],"summary":"Summary report by category (CSV)","description":"Counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-category.json","tags":["events"],"summary":"Deep summary report by category","description":"seats.io deep summary by category: the buckets of the summary by category, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category-key.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category-key.json","tags":["events"],"summary":"Summary report by category key","description":"Counts grouped by categoryKey. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category-key.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category-key.csv","tags":["events"],"summary":"Summary report by category key (CSV)","description":"Counts grouped by categoryKey. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-category-key.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-category-key.json","tags":["events"],"summary":"Deep summary report by category key","description":"seats.io deep summary by categoryKey: the buckets of the summary by categoryKey, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category-label.json","tags":["events"],"summary":"Summary report by category label","description":"Counts grouped by categoryLabel. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-category-label.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-category-label.csv","tags":["events"],"summary":"Summary report by category label (CSV)","description":"Counts grouped by categoryLabel. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-category-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-category-label.json","tags":["events"],"summary":"Deep summary report by category label","description":"seats.io deep summary by categoryLabel: the buckets of the summary by categoryLabel, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-section.json","tags":["events"],"summary":"Summary report by section","description":"Counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-section.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-section.csv","tags":["events"],"summary":"Summary report by section (CSV)","description":"Counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-section.json","tags":["events"],"summary":"Deep summary report by section","description":"seats.io deep summary by section: the buckets of the summary by section, each with `deep` — one full summary bucket per status (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-zone.json","tags":["events"],"summary":"Summary report by zone","description":"Counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-zone.csv","tags":["events"],"summary":"Summary report by zone (CSV)","description":"Counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-zone.json","tags":["events"],"summary":"Deep summary report by zone","description":"seats.io deep summary by zone: the buckets of the summary by zone, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-channel.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-channel.json","tags":["events"],"summary":"Summary report by channel","description":"Counts grouped by channel. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-channel.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-channel.csv","tags":["events"],"summary":"Summary report by channel (CSV)","description":"Counts grouped by channel. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-channel.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-channel.json","tags":["events"],"summary":"Deep summary report by channel","description":"seats.io deep summary by channel: the buckets of the summary by channel, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-object-type.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-object-type.json","tags":["events"],"summary":"Summary report by object type","description":"Counts grouped by objectType. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-object-type.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-object-type.csv","tags":["events"],"summary":"Summary report by object type (CSV)","description":"Counts grouped by objectType. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-object-type.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-object-type.json","tags":["events"],"summary":"Deep summary report by object type","description":"seats.io deep summary by objectType: the buckets of the summary by objectType, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-availability.json","tags":["events"],"summary":"Summary report by availability","description":"Counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-availability.csv","tags":["events"],"summary":"Summary report by availability (CSV)","description":"Counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-availability.json","tags":["events"],"summary":"Deep summary report by availability","description":"seats.io deep summary by availability: the buckets of the summary by availability, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-availability-reason.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-availability-reason.json","tags":["events"],"summary":"Summary report by availability reason","description":"Counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary/by-availability-reason.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsSummaryBy-availability-reason.csv","tags":["events"],"summary":"Summary report by availability reason (CSV)","description":"Counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary/by-availability-reason.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false}],"operationId":"getApiEventsByEventIdReportsDeep-summaryBy-availability-reason.json","tags":["events"],"summary":"Deep summary report by availability reason","description":"seats.io deep summary by availabilityReason: the buckets of the summary by availabilityReason, each with `deep` — one full summary bucket per section (named in `deepBy`). Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummary.json","tags":["seasons"],"summary":"Season summary across member events","description":"`totals`: season-level availability (an object counts as available only when free in every evaluated event). `events`: each member event's own totals (date order) with `isInThePast` and `evaluated`. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-status.json","tags":["seasons"],"summary":"Season report by status","description":"Objects of the season grouped by status. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-category.json","tags":["seasons"],"summary":"Season report by category","description":"Objects of the season grouped by category. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-availability.json","tags":["seasons"],"summary":"Season report by availability","description":"Objects of the season grouped by availability. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-zone.json","tags":["seasons"],"summary":"Season report by zone","description":"Objects of the season grouped by zone. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-status.json","tags":["seasons"],"summary":"Season summary by status","description":"Season-level counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-category.json","tags":["seasons"],"summary":"Season summary by category","description":"Season-level counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-availability.json","tags":["seasons"],"summary":"Season summary by availability","description":"Season-level counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-availability-reason.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-availability-reason.json","tags":["seasons"],"summary":"Season summary by availability reason","description":"Season-level counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-section.json","tags":["seasons"],"summary":"Season summary by section","description":"Season-level counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-zone.json","tags":["seasons"],"summary":"Season summary by zone","description":"Season-level counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummary.csv","tags":["seasons"],"summary":"Season summary across member events (CSV)","description":"`totals`: season-level availability (an object counts as available only when free in every evaluated event). `events`: each member event's own totals (date order) with `isInThePast` and `evaluated`. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-status.csv","tags":["seasons"],"summary":"Season report by status (CSV)","description":"Objects of the season grouped by status. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-category.csv","tags":["seasons"],"summary":"Season report by category (CSV)","description":"Objects of the season grouped by category. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-availability.csv","tags":["seasons"],"summary":"Season report by availability (CSV)","description":"Objects of the season grouped by availability. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsBy-zone.csv","tags":["seasons"],"summary":"Season report by zone (CSV)","description":"Objects of the season grouped by zone. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-status.csv","tags":["seasons"],"summary":"Season summary by status (CSV)","description":"Season-level counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-category.csv","tags":["seasons"],"summary":"Season summary by category (CSV)","description":"Season-level counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-availability.csv","tags":["seasons"],"summary":"Season summary by availability (CSV)","description":"Season-level counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-availability-reason.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-availability-reason.csv","tags":["seasons"],"summary":"Season summary by availability reason (CSV)","description":"Season-level counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-section.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-section.csv","tags":["seasons"],"summary":"Season summary by section (CSV)","description":"Season-level counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/reports/summary/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdReportsSummaryBy-zone.csv","tags":["seasons"],"summary":"Season summary by zone (CSV)","description":"Season-level counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummary.json","tags":["seasons"],"summary":"Partial season summary across member events","description":"`totals`: season-level availability (an object counts as available only when free in every evaluated event). `events`: each member event's own totals (date order) with `isInThePast` and `evaluated`. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-status.json","tags":["seasons"],"summary":"Partial season report by status","description":"Objects of the partial season grouped by status. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-category.json","tags":["seasons"],"summary":"Partial season report by category","description":"Objects of the partial season grouped by category. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-availability.json","tags":["seasons"],"summary":"Partial season report by availability","description":"Objects of the partial season grouped by availability. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-zone.json","tags":["seasons"],"summary":"Partial season report by zone","description":"Objects of the partial season grouped by zone. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-status.json","tags":["seasons"],"summary":"Partial season summary by status","description":"Season-level counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-category.json","tags":["seasons"],"summary":"Partial season summary by category","description":"Season-level counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-availability.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-availability.json","tags":["seasons"],"summary":"Partial season summary by availability","description":"Season-level counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-availability-reason.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-availability-reason.json","tags":["seasons"],"summary":"Partial season summary by availability reason","description":"Season-level counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-section.json","tags":["seasons"],"summary":"Partial season summary by section","description":"Season-level counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-zone.json","tags":["seasons"],"summary":"Partial season summary by zone","description":"Season-level counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummary.csv","tags":["seasons"],"summary":"Partial season summary across member events (CSV)","description":"`totals`: season-level availability (an object counts as available only when free in every evaluated event). `events`: each member event's own totals (date order) with `isInThePast` and `evaluated`. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this status bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"status","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-status.csv","tags":["seasons"],"summary":"Partial season report by status (CSV)","description":"Objects of the partial season grouped by status. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-category.csv","tags":["seasons"],"summary":"Partial season report by category (CSV)","description":"Objects of the partial season grouped by category. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this availability bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"availability","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-availability.csv","tags":["seasons"],"summary":"Partial season report by availability (CSV)","description":"Objects of the partial season grouped by availability. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsBy-zone.csv","tags":["seasons"],"summary":"Partial season report by zone (CSV)","description":"Objects of the partial season grouped by zone. Objects are matched by label across the member events (same rule as season booking). Evaluated events are the live (not past) members — all members when none is live — or, with `seasonBookingsPropagated=false` (the default for seasons whose bookings do not propagate), the season source event only. Rows: `label`, `objectType`, `category`, `section`, `zone` (from the source event), `status` (shared status or `mixed`), `availability` (available only when free in every evaluated event), `availabilityReason` (the most definitive blocking reason; a label missing from an event counts as not_for_sale), `eventCount`, `availableIn`, `notAvailableEventIds`; GA rows add `capacity` and `availableCount` (places free in every evaluated event). `events` lists every member with `evaluated`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-status.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-status.csv","tags":["seasons"],"summary":"Partial season summary by status (CSV)","description":"Season-level counts grouped by status. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-category.csv","tags":["seasons"],"summary":"Partial season summary by category (CSV)","description":"Season-level counts grouped by category. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-availability.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-availability.csv","tags":["seasons"],"summary":"Partial season summary by availability (CSV)","description":"Season-level counts grouped by availability. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-availability-reason.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-availability-reason.csv","tags":["seasons"],"summary":"Partial season summary by availability reason (CSV)","description":"Season-level counts grouped by availabilityReason. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-section.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-section.csv","tags":["seasons"],"summary":"Partial season summary by section (CSV)","description":"Season-level counts grouped by section. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/reports/summary/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"partialSeasonId","required":true},{"description":"Merge active buyer holds from the per-event hold index (default `true`). `false` = Postgres only.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"liveHolds","required":false},{"description":"seats.io `seasonBookingsPropagated`: scope of the bookings in the season-level rows (default: the season `bookingsPropagated` setting). `true` = an object is available only when free in every evaluated member event. `false` = only the bookings of the season itself count: its own inventory, the source event (when it is part of the report; otherwise every member is evaluated as with `true`). Per-event totals are unchanged.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"}]},"in":"query","name":"seasonBookingsPropagated","required":false}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdReportsSummaryBy-zone.csv","tags":["seasons"],"summary":"Partial season summary by zone (CSV)","description":"Season-level counts grouped by zone. Each bucket: `count` (places — 1 per seat, capacity per GA area), `objects`, `seats`, `gaPlaces`, and nested `byStatus`, `byAvailability`, `byAvailabilityReason`, `byCategory`, `bySection`, `byZone`, `byChannel`, `byObjectType`. GA areas are split by place, so one area can contribute to several availability buckets. `totals` sums every bucket. Missing keys use NO_CATEGORY / NO_SECTION / NO_ZONE / NO_CHANNEL. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). At most 100 member events. Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this label bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"label","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-label.json","tags":["charts"],"summary":"Chart report by label","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category.json","tags":["charts"],"summary":"Chart report by category","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category-key.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this categoryKey bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryKey","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category-key.json","tags":["charts"],"summary":"Chart report by category key","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this categoryLabel bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryLabel","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category-label.json","tags":["charts"],"summary":"Chart report by category label","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this section bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"section","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-section.json","tags":["charts"],"summary":"Chart report by section","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-zone.json","tags":["charts"],"summary":"Chart report by zone","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-object-type.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this objectType bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"objectType","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-object-type.json","tags":["charts"],"summary":"Chart report by object type","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category.json","tags":["charts"],"summary":"Chart summary by category","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category-key.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category-key.json","tags":["charts"],"summary":"Chart summary by category key","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category-label.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category-label.json","tags":["charts"],"summary":"Chart summary by category label","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-section.json","tags":["charts"],"summary":"Chart summary by section","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-zone.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-zone.json","tags":["charts"],"summary":"Chart summary by zone","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-object-type.json":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-object-type.json","tags":["charts"],"summary":"Chart summary by object type","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-label.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this label bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"label","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-label.csv","tags":["charts"],"summary":"Chart report by label (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this category bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"category","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category.csv","tags":["charts"],"summary":"Chart report by category (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category-key.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this categoryKey bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryKey","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category-key.csv","tags":["charts"],"summary":"Chart report by category key (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-category-label.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this categoryLabel bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"categoryLabel","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-category-label.csv","tags":["charts"],"summary":"Chart report by category label (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-section.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this section bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"section","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-section.csv","tags":["charts"],"summary":"Chart report by section (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this zone bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"zone","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-zone.csv","tags":["charts"],"summary":"Chart report by zone (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/by-object-type.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"Only this objectType bucket.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"objectType","required":false},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsBy-object-type.csv","tags":["charts"],"summary":"Chart report by object type (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category.csv","tags":["charts"],"summary":"Chart summary by category (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category-key.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category-key.csv","tags":["charts"],"summary":"Chart summary by category key (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-category-label.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-category-label.csv","tags":["charts"],"summary":"Chart summary by category label (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-section.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-section.csv","tags":["charts"],"summary":"Chart summary by section (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-zone.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-zone.csv","tags":["charts"],"summary":"Chart summary by zone (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/charts/{chartId}/reports/summary/by-object-type.csv":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"chartId","required":true},{"description":"`published` (default) or `draft` (the unpublished designer draft).","schema":{"anyOf":[{"const":"published","type":"string"},{"const":"draft","type":"string"}]},"in":"query","name":"version","required":false},{"description":"seats.io `bookWholeTables`: `false` (default) lists every seat; `true` lists every table as one object (`objectType: table`, `capacity` = its seats, `seats` = their labels, `price` = their sum) instead of its seats; `chart` does so for the tables the chart books as a whole (table booking `all_by_table`, or `custom` tables set to `by_table`). Echoed as `bookWholeTables`.","schema":{"anyOf":[{"const":"true","type":"string"},{"const":"false","type":"string"},{"const":"chart","type":"string"}]},"in":"query","name":"bookWholeTables","required":false}],"operationId":"getApiChartsByChartIdReportsSummaryBy-object-type.csv","tags":["charts"],"summary":"Chart summary by object type (CSV)","description":"Reads the published chart (seat and GA templates plus the published chart layout) — no event, no live state. Rows: `objectId`, `label`, `objectType`, `category`, `section`, `zone`, `capacity` (1 per seat, places per GA area), `price` (catalog cents; null for GA), `categoryKey` (chart category id of `category`; the value itself when no category defines it). `published` is false when the chart has never been published (no rows). `?version=draft` reports the unpublished designer draft instead (404 `draft_not_found` when the chart has none): it reads only the draft row, never the published geometry or its caches; `version` echoes the reported version. GA areas sold as a whole add `areaType`, `bookAsAWhole` and (variable occupancy) `minOccupancy` / `maxOccupancy`. Buckets: `count` (places), `objects`, `seats`, `gaPlaces`, `byCategory`, `bySection`, `byZone`, `byObjectType`; `totals` sums every bucket. The `.csv` variant returns the same data as RFC 4180 CSV (one line per object, or per group for summaries). Requires content_write (or a partner API key with the reports scope).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/occupancy.csv":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsOccupancy.csv","tags":["events"],"summary":"Export seat and GA occupancy as CSV","description":"One row per numbered seat or GA area with status and capacity counts. Requires map editor/admin (or partner API key) — not B2C viewer JWTs.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/sales.csv":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsSales.csv","tags":["events"],"summary":"Export confirmed seat and GA sales as CSV","description":"One row per confirmed reservation (numbered seats and GA). Includes buyer user_id — requires map editor/admin (or partner API key).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/summary.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsSummary.json","tags":["events"],"summary":"Occupancy summary JSON (Postgres only)","description":"Aggregated seat + GA counts from Postgres. Does not MGET Redis holds. Requires content_write.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-status.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsBy-status.json","tags":["events"],"summary":"Seat counts by Postgres status","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-category.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsBy-category.json","tags":["events"],"summary":"Seat and GA counts by category","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/deep-summary.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdReportsDeep-summary.json","tags":["events"],"summary":"Deep summary: status counts nested by category","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-label.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","maxLength":64},"in":"query","name":"label","required":false}],"operationId":"getApiEventsByEventIdReportsBy-label.json","tags":["events"],"summary":"Detailed report grouped by object label (Postgres)","description":"Seats.io-style byLabel. Optional `?label=` filters to one label. Never reads Redis.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-order-id.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","maxLength":255},"in":"query","name":"orderId","required":false}],"operationId":"getApiEventsByEventIdReportsBy-order-id.json","tags":["events"],"summary":"Detailed report grouped by orderId (Postgres)","description":"Uses object_statuses.order_id. Missing order → bucket NO_ORDER_ID. Optional `?orderId=`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-channel.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","maxLength":36},"in":"query","name":"channelKeyId","required":false}],"operationId":"getApiEventsByEventIdReportsBy-channel.json","tags":["events"],"summary":"Detailed report grouped by inventory channel","description":"Null channel → NO_CHANNEL. Optional `?channelKeyId=`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-object-type.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"description":"Only this object type (seats.io `byObjectType(event, type)`).","schema":{"anyOf":[{"const":"seat","type":"string"},{"const":"ga","type":"string"}]},"in":"query","name":"objectType","required":false}],"operationId":"getApiEventsByEventIdReportsBy-object-type.json","tags":["events"],"summary":"Detailed report grouped by object type (seat|ga)","description":"Buckets `seat` and `ga` with their objects (Postgres). Optional `?objectType=seat|ga` returns only that bucket.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/reports/by-section.json":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","maxLength":128},"in":"query","name":"section","required":false}],"operationId":"getApiEventsByEventIdReportsBy-section.json","tags":["events"],"summary":"Detailed report grouped by section","description":"Groups seats by layout sectionGuide name (geometry join via pos_x/pos_y). Seats outside all guides and GA areas → NO_SECTION.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/map":{"put":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdMap","tags":["events"],"summary":"Save map draft (legacy path)","description":"Alias for **`PUT /map/draft`**. Does **not** publish to buyers — use **`POST /map/publish`**.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/events/{eventId}/map/draft":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdMapDraft","tags":["events"],"summary":"Load map draft for designer","description":"Returns unpublished draft when present; otherwise bootstraps from the published map. Does not affect the B2C viewer.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"put":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdMapDraft","tags":["events"],"summary":"Save map draft (does not go live)","description":"Persists designer changes to `event_map_drafts` only. B2C viewers keep reading the last published map until `POST .../map/publish`. With `baseDraftUpdatedAt` the save is conflict-safe: 409 `draft_conflict` (with the current `draftUpdatedAt`) when the draft changed since that version (designer autosave).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["seats"],"properties":{"seats":{"maxItems":50000,"type":"array","items":{"type":"object","required":["label","pos_x","pos_y"],"properties":{"id":{"maxLength":36,"type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"pos_x":{"minimum":-10000,"maximum":10000,"type":"number"},"pos_y":{"minimum":-10000,"maximum":10000,"type":"number"},"category":{"maxLength":64,"type":"string"},"price":{"minimum":0,"maximum":99999999,"type":"number"},"rowLabel":{"maxLength":64,"description":"Row label (structural; e.g. \"A\").","type":"string"},"sectionId":{"maxLength":64,"description":"Section scope (structural). Seat labels must be unique within a section.","type":"string"},"displayedLabel":{"maxLength":64,"description":"Short label printed on the seat (e.g. \"12\"). `label` stays the API identity.","type":"string"},"accessibility":{"additionalProperties":false,"type":"object","properties":{"wheelchair":{"description":"Wheelchair space.","type":"boolean"},"companion":{"description":"Companion seat next to a wheelchair space.","type":"boolean"},"restrictedView":{"description":"Partially restricted view.","type":"boolean"},"obstructedView":{"description":"View obstructed (e.g. by a pillar).","type":"boolean"},"viewNote":{"maxLength":280,"description":"Short buyer-facing note about the view.","type":"string"},"amenities":{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":8191,"description":"Compact bitmask of the other accessibility and amenity flags: 16 wheelchair space without a seat in place (requires wheelchair), 32 semi-ambulatory, 64 plus-size, 128 hearing impaired (CART services), 256 hearing impaired (sign language interpretation), 512 not suitable for vertigo sufferers, 1024 limited legroom, 2048 lift-up armrests, 4096 aisle seat. Bits 1/2/4/8 mirror wheelchair / companion / restrictedView / obstructedView, which are always stored and returned as booleans.","type":"integer"}]},"seatType":{"description":"Seat display type. \"chair\" is the default and is not stored.","anyOf":[{"const":"chair","type":"string"},{"const":"stool","type":"string"},{"const":"bench","type":"string"},{"const":"couch","type":"string"}]}}},"entrance":{"maxLength":64,"description":"Entrance shown to buyers (e.g. \"Gate 3\"). Rows (layoutMeta.rowSettings), tables, sections and GA areas (an \"entrance\" field on their layout shape) can set one too; the most specific wins: seat, row, table, section.","type":"string"}}}},"shapes":{"maxItems":5000,"description":"Layout shapes (sections, zones, GA areas, tables, icons…) and one `layoutMeta`. `layoutMeta.settings3d` holds the authored 3D (elevation / rake per section or zone, floors, light preset, spectator target) and the 3D billboards: `billboards` (at most 64 boards, each `{ id, points: [{x, y}] (2-64, map px), height?: m (0.2-20, default 1), elevation?: m (-20-100), scroll?: \"reverse\" | \"off\" }`) and `billboardImages` (at most 12 sponsor images shared by every board, in scroll order: base64 WebP / PNG / JPEG data URLs, stored to the map-asset CDN when configured, or map-asset URLs). Invalid values are refused with 400 `validation_error`, never clamped. `settings3d.seatPicking` (boolean) is the chart-level \"Allow 3D seat picking\" switch (absent: on for charts that author 3D). Optional fields published only when set: tables `oneSide` (rectangular, every guest on one long side) and `openSpaces` (1-20 empty chair positions); text `bold` / `italic`; decorative shapes `layer: \"foreground\"` (drawn above the seats for buyers, never interactive); `layoutMeta.floors[].abbreviation` (at most 6 characters) and `layoutMeta.displayTypes` (object id → displayed type, at most 64 characters, seats.io `displayObjectType`). Designer-only metadata (`bytes` on images, `layoutMeta.backgroundBytes`) feeds the `images_size` check and is never published.","type":"array","items":{}},"background":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"maxLength":2000000,"type":"string"},{"type":"null"}]},"baseDraftUpdatedAt":{"description":"Draft version this save is based on (`draftUpdatedAt` from the last load or save; null for a never-saved draft). When given and the stored draft changed meanwhile, the save is refused with 409 `draft_conflict` (designer autosave). Omit it for last-writer-wins.","anyOf":[{"maxLength":40,"type":"string"},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/events/{eventId}/map/publish":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"responses":{"422":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"const":"validation_failed","type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","severity","objectIds","message","count"],"properties":{"code":{"description":"Chart validation rule id.","anyOf":[{"const":"no_objects","type":"string"},{"const":"empty_label","type":"string"},{"const":"duplicate_label","type":"string"},{"const":"uncategorized","type":"string"},{"const":"overlap","type":"string"},{"const":"table_too_small","type":"string"},{"const":"ga_invalid_capacity","type":"string"},{"const":"ga_table_capacity","type":"string"},{"const":"empty_floor","type":"string"},{"const":"empty_section","type":"string"},{"const":"outside_section","type":"string"},{"const":"multiple_object_types","type":"string"},{"const":"missing_focal_point","type":"string"},{"const":"zone_missing_focal_point","type":"string"},{"const":"orphan_gap","type":"string"},{"const":"images_size","type":"string"}]},"severity":{"anyOf":[{"const":"error","type":"string"},{"const":"warning","type":"string"}]},"objectIds":{"description":"Seat, GA area, section, zone or floor ids, depending on the code.","type":"array","items":{"type":"string"}},"message":{"type":"string"},"count":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"label":{"type":"string"},"sectionId":{"type":"string"},"tableIds":{"type":"array","items":{"type":"string"}},"scope":{"description":"Section or floor name the offenders belong to (empty = whole map).","type":"string"},"floorId":{"type":"string"},"sections":{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":2,"description":"`duplicate_label` across sections: how many sections sharing the label `sectionId` (or unlabeled sections) hold the object.","type":"integer"}]},"bytes":{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"`images_size`: known total download size of the chart images, in bytes.","type":"integer"}]}}}}},"required":["error","message","issues"]}}}}},"operationId":"postApiEventsByEventIdMapPublish","tags":["events"],"summary":"Publish draft map to live seats","description":"Promotes draft → `seats` + `event_layouts`, invalidates map cache. Blocks publish if draft removes sold seats.\n\nA draft that removes seats of active listings is refused with 409 `listing_objects_removed` (`listings`: id, key and `removedObjects` of each affected listing): withdraw or update those listings first, or publish with `withdrawAffectedListings: true` to withdraw them in the same transaction (409 `listing_has_holds` while any of their objects is held). Withdrawn listings are returned in `withdrawnListings` and recorded in the event log.\n\nThe draft is validated with the same rules the designer runs: duplicate seat labels within a section, seats without a category (when categories exist), empty labels, overlapping seats and tables with fewer than 2 seats are **errors** and return `422 { error: \"validation_failed\", issues: [{ code, severity, objectIds, message }] }`. Warnings (one-seat row gaps) do not block and are returned in `warnings`.\n\nEvery successful publish appends an immutable version (`version`; `sameAsVersion` names an earlier version with identical content). The map that was live before the first recorded publish becomes the `baseline` version 1. See `GET /api/events/:eventId/map/versions`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"withdrawAffectedListings":{"description":"When the draft removes seats of active listings, withdraw those listings in the publish transaction (refused with 409 `listing_has_holds` while any object of them is held). Without it such a publish is refused with 409 `listing_objects_removed` (`listings`: id, key and removed objects of each affected listing).","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"withdrawAffectedListings":{"description":"When the draft removes seats of active listings, withdraw those listings in the publish transaction (refused with 409 `listing_has_holds` while any object of them is held). Without it such a publish is refused with 409 `listing_objects_removed` (`listings`: id, key and removed objects of each affected listing).","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"withdrawAffectedListings":{"description":"When the draft removes seats of active listings, withdraw those listings in the publish transaction (refused with 409 `listing_has_holds` while any object of them is held). Without it such a publish is refused with 409 `listing_objects_removed` (`listings`: id, key and removed objects of each affected listing).","type":"boolean"}}}}}}}},"/api/events/{eventId}/map/draft/actions/discard":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdMapDraftActionsDiscard","tags":["events"],"summary":"Discard event map draft","description":"Throws away unpublished designer changes so `GET .../map/draft` returns the live map (the seats buyers see). Seats, inventory, holds and the B2C viewer are untouched. `409 ERR_DRAFT_NOTHING_TO_DISCARD` when there is no draft with unpublished changes.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/map/versions":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before the first recorded publish (cloned from a chart, or published before history).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"thumbnailUrl":{"description":"Relative API path (authenticated) of the placeholder preview of this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before the first recorded publish (cloned from a chart, or published before history).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"thumbnailUrl":{"description":"Relative API path (authenticated) of the placeholder preview of this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}},"text/plain":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["version","kind","geometryHash","publishedAt","publishedBy","seatCount","gaAreaCount","thumbnailUrl","isCurrent"],"properties":{"version":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"kind":{"description":"`baseline` = the map that was live before the first recorded publish (cloned from a chart, or published before history).","anyOf":[{"const":"publish","type":"string"},{"const":"baseline","type":"string"}]},"geometryHash":{"description":"SHA-256 of the canonical snapshot. Equal hashes mean identical published content.","type":"string"},"publishedAt":{"format":"date-time","type":"string"},"publishedBy":{"type":"object","required":["type","userId","label"],"properties":{"type":{"anyOf":[{"const":"user","type":"string"},{"const":"api_key","type":"string"},{"const":"system","type":"string"}]},"userId":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"description":"Dashboard user email at publish time; null for API keys and system.","anyOf":[{"type":"string"},{"type":"null"}]}}},"seatCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"gaAreaCount":{"minimum":0,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"type":"integer"}]},"thumbnailUrl":{"description":"Relative API path (authenticated) of the placeholder preview of this version.","type":"string"},"isCurrent":{"description":"True for the newest version (the one currently published).","type":"boolean"}}}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersion":{"anyOf":[{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},{"type":"null"}]}},"required":["items","nextCursor","currentVersion"]}}}},"400":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"403":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"404":{"description":"Error body `{ error, code, message, params? }` (stable `code`, English `message`).","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}}},"operationId":"getApiEventsByEventIdMapVersions","tags":["events"],"summary":"List event map versions","description":"Immutable publish history of the event seat map, newest first, cursor-paginated (`limit` 1..100, default 20; pass `nextCursor` back as `cursor`). Every successful `POST /api/events/:eventId/map/publish` appends one version; snapshots are content-addressed (identical content shares a `geometryHash`). The map that was live before the first recorded publish is kept as the `baseline` version 1.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/events/{eventId}/map/versions/{version}/thumbnail":{"get":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Version number (1-based).","schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"description":"Version number (1-based).","type":"number"}]},"in":"path","name":"version","required":true}],"operationId":"getApiEventsByEventIdMapVersionsByVersionThumbnail","tags":["events"],"summary":"Event map version preview","description":"PNG preview of one version: zone / section / GA outlines, decorative shapes and category-coloured seat dots (first floor of multi-floor maps), rendered server-side and cached by content hash. Immutable: `Cache-Control: private, max-age=31536000, immutable` with an `ETag` (send `If-None-Match` for `304`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/map/versions/{version}/actions/restore-to-draft":{"post":{"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"path","name":"eventId","required":true},{"description":"Version number (1-based).","schema":{"minimum":1,"maximum":2147483647,"multipleOf":1,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2147483647,"multipleOf":1,"description":"Version number (1-based).","type":"number"}]},"in":"path","name":"version","required":true}],"operationId":"postApiEventsByEventIdMapVersionsByVersionActionsRestore-to-draft","tags":["events"],"summary":"Restore an event map version into the draft","description":"Overwrites the event map draft with the content of a published version (seats, shapes, categories, images). It never publishes: buyers keep the live map until `POST /api/events/:eventId/map/publish`, which runs the normal validation and sold-seat checks and records a new version. Unpublished draft changes are replaced. Requires map authoring rights (admin / map editor dashboard role or `x-api-key` with `charts_write`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/inventory/for-sale/apply":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdInventoryFor-saleApply","tags":["events"],"summary":"Apply for-sale / not-for-sale to a selection (incremental)","description":"Batch-set `for_sale` on seats and/or GA areas (`seatIds`, `gaAreaIds`, and/or whole `categories`). When marking not-for-sale, releases active Redis seat holds and clears GA hold hashes for those objects. `areaPlaces` (`{ gaAreaId: places }`) withholds (`forSale: false`) or returns (`forSale: true`) a number of places of a GA area without closing it: withholding is checked under the GA row lock against sold and held places (409 `insufficient_free_places`), and new holds then use capacity minus withheld places. `everything: true` (alone) puts every seat (except retired ones) and GA area back on sale and clears withheld places. Category toggles never put retired seats back on sale. Invalidates the seat-map cache and publishes inventory SSE (`kind: inventory`, `type: for_sale`) so connected viewers can apply NFS locally. Optional `cascadeSeason` (default false) syncs live season siblings when this event is the source (NFS + channels + object_statuses; past members skipped; members with `forSalePropagated: false` keep their own for-sale flags). Inheritance is cascade-on-write, not a second table. On a season member that is not the source, this call sets the membership's `forSalePropagated` to false (response `forSalePropagated: false` when it changed): restore it with PATCH /seasons/{seasonId}/events/{eventId}. Response includes `notForSalePlaces` (resulting withheld places per GA area touched). Requires partner API key or dashboard JWT with map authoring permission.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"forSale":{"type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"everything":{"type":"boolean"},"cascadeSeason":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"forSale":{"type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"everything":{"type":"boolean"},"cascadeSeason":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"forSale":{"type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"everything":{"type":"boolean"},"cascadeSeason":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/inventory/for-sale":{"put":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdInventoryFor-sale","tags":["events"],"summary":"Replace the whole for-sale configuration atomically","description":"Seats.io `replaceForSaleConfig` in **one SQL transaction** (all or nothing): the listed seats (`seatIds`), GA areas (`gaAreaIds`, or keys of `areaPlaces`) and every object of the listed `categories` take `forSale`, every other seat and GA area of the event takes the opposite. `areaPlaces` sets the withheld places of a listed area (`forSale: false`: that many places off sale; `forSale: true`: only that many on sale); unlisted areas keep no withheld places. Retired seats always stay off sale. GA rows are locked and withheld places are checked against sold and held places (409 `insufficient_free_places`, nothing written). Holds on objects that went off sale are released after COMMIT; one cache invalidate and one `for_sale` realtime message per direction. Empty lists with `forSale: false` put everything on sale. Response: `changedSeats`, `changedGaAreas`, `seatsNotForSale`, `gaAreasNotForSale`, `notForSalePlaces`, `releasedSeatHolds`, `clearedGaHoldAreas`. Season behaviour as POST …/for-sale/apply (`cascadeSeason`, `forSalePropagated`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects are not for sale, everything else is. `true`: only the listed objects are for sale.","type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area id → places: withheld (`forSale: false`) or the only places on sale (`forSale: true`).","type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects are not for sale, everything else is. `true`: only the listed objects are for sale.","type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area id → places: withheld (`forSale: false`) or the only places on sale (`forSale: true`).","type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects are not for sale, everything else is. `true`: only the listed objects are for sale.","type":"boolean"},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"categories":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area id → places: withheld (`forSale: false`) or the only places on sale (`forSale: true`).","type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/edit-for-sale-config":{"post":{"parameters":[{"description":"Optional. A replay with the same key and body returns the stored response (24 h); a different body under the same key is 409 `idempotency_conflict`.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsEdit-for-sale-config","tags":["events"],"summary":"Edit the for-sale configuration (forSale and notForSale in one transaction)","description":"Seats.io `editForSaleConfig`. Incremental: only the listed objects, GA places and categories change, everything else keeps its state, and both lists are applied in **one SQL transaction** (all or nothing; the event row lock serializes it with the replace and mark-everything calls). Items are `{ object }` (seat UUID or label, GA area UUID or name), `{ object, quantity }` (GA places: `notForSale` withholds that many more places, checked under the GA row lock against sold and held places — 409 `insufficient_free_places`; `forSale` returns that many withheld places) or `{ category }`. An explicit object wins over its category; retired seats never go back on sale; sold seats stay sold (only their flag changes). The same object or category in both lists is 400 `for_sale_edit_conflict`; unknown objects 404 `objects_not_found`; a label that names several objects 409 `ambiguous_label`. After COMMIT the holds on objects that went off sale are released, the seat-map cache is invalidated once and `for_sale` realtime messages are published (GA place changes as GA updates); a confirmation racing the change never sells an object that went off sale (the SQL decision re-checks under the seat row lock). Audited as `inventory.for_sale.edit` (event log and `event.for_sale_changed` webhook, alias `event.for.sale.config.updated`). On a season member that is not the source, the membership's `forSalePropagated` becomes false, as with the other for-sale calls. Optional `Idempotency-Key`. Not rate limited per event (seats.io allows 10 calls per event and day in its RATE_LIMITED for-sale mode). Response: `forSaleConfig` (the resulting configuration as seats.io returns it: `forSale: false`, `objects` (labels of the seats and names of the GA areas that are off sale), `categories` (categories whose every object is off sale; their objects are not repeated in `objects`) and `areaPlaces` (GA area name → withheld places of an area on sale)), `changedSeats`, `changedGaAreas`, `seatsNotForSale`, `gaAreasNotForSale`, `notForSalePlaces` (GA area id → withheld places of the areas changed by quantity), `releasedSeatHolds`, `clearedGaHoldAreas`, `seasonCascade`, `forSalePropagated` (false when this call detached a season member) and `realtime`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/actions/edit-for-sale-config":{"post":{"parameters":[{"description":"Optional. A replay with the same key and body returns the stored response (24 h); a different body under the same key is 409 `idempotency_conflict`.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":false}],"operationId":"postApiEventsActionsEdit-for-sale-config","tags":["events"],"summary":"Edit the for-sale configuration of several events (all or nothing)","description":"Seats.io `editForSaleConfigForEvents`: `events[]` (1–100, each event once, at most 20000 items overall) with `event` (UUID or key), `forSale`, `notForSale` and `cascadeSeason`, applied to every event in **one SQL transaction**: either every event changes or none does (events are locked in id order, so overlapping calls serialize). Per event: Incremental: only the listed objects, GA places and categories change, everything else keeps its state, and both lists are applied in **one SQL transaction** (all or nothing; the event row lock serializes it with the replace and mark-everything calls). Items are `{ object }` (seat UUID or label, GA area UUID or name), `{ object, quantity }` (GA places: `notForSale` withholds that many more places, checked under the GA row lock against sold and held places — 409 `insufficient_free_places`; `forSale` returns that many withheld places) or `{ category }`. An explicit object wins over its category; retired seats never go back on sale; sold seats stay sold (only their flag changes). The same object or category in both lists is 400 `for_sale_edit_conflict`; unknown objects 404 `objects_not_found`; a label that names several objects 409 `ambiguous_label`. After COMMIT the holds on objects that went off sale are released, the seat-map cache is invalidated once and `for_sale` realtime messages are published (GA place changes as GA updates); a confirmation racing the change never sells an object that went off sale (the SQL decision re-checks under the seat row lock). Audited as `inventory.for_sale.edit` (event log and `event.for_sale_changed` webhook, alias `event.for.sale.config.updated`). On a season member that is not the source, the membership's `forSalePropagated` becomes false, as with the other for-sale calls. Optional `Idempotency-Key`. Not rate limited per event (seats.io allows 10 calls per event and day in its RATE_LIMITED for-sale mode). Response `{ ok, results: [{ index, event, eventId, forSaleConfig, changedSeats, …, seasonCascade }], realtime }` in request order. On any refusal nothing is written and the status is that of the failing event, with its `index` and `event` in the body.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["events"],"properties":{"events":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["event"],"properties":{"event":{"minLength":1,"maxLength":100,"description":"Event UUID or event key.","type":"string"},"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["events"],"properties":{"events":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["event"],"properties":{"event":{"minLength":1,"maxLength":100,"description":"Event UUID or event key.","type":"string"},"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}}},"text/plain":{"schema":{"type":"object","required":["events"],"properties":{"events":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["event"],"properties":{"event":{"minLength":1,"maxLength":100,"description":"Event UUID or event key.","type":"string"},"forSale":{"maxItems":10000,"description":"Objects, GA places and categories to put on sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"notForSale":{"maxItems":10000,"description":"Objects, GA places and categories to take off sale.","type":"array","items":{"additionalProperties":false,"type":"object","properties":{"object":{"minLength":1,"maxLength":128,"description":"Seat UUID or label, or GA area UUID or name (exactly one of object / category).","type":"string"},"quantity":{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"description":"GA places: `notForSale` withholds this many more places, `forSale` returns this many withheld places.","type":"integer"}]},"category":{"minLength":1,"maxLength":64,"description":"Every seat and GA area of this category.","type":"string"}}}},"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/mark-everything-as-not-for-sale":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsMark-everything-as-not-for-sale","tags":["events"],"summary":"Mark every object of the event not for sale","description":"Seats.io `markEverythingAsNotForSale`: every seat and GA area of the event goes off sale in **one SQL transaction** (the event row lock serializes it with other for-sale changes; all or nothing). Holds on the objects are released after COMMIT, then one cache invalidate and one `for_sale` realtime message. Sold seats stay sold. Audited (`inventory.for_sale.mark_everything_not_for_sale`), in the event log and the `event.for_sale_changed` webhook (`event.for.sale.config.updated` alias). Season behaviour as POST …/for-sale/apply (`cascadeSeason`, `forSalePropagated`). Response as PUT …/inventory/for-sale (`changedSeats`, `changedGaAreas`, `seatsNotForSale`, `gaAreasNotForSale`, `releasedSeatHolds`, `clearedGaHoldAreas`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/mark-everything-as-for-sale":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsMark-everything-as-for-sale","tags":["events"],"summary":"Put every object of the event back on sale","description":"Seats.io `markEverythingAsForSale`: the inverse of mark-everything-as-not-for-sale, in one SQL transaction — every seat (except retired ones) and GA area goes on sale and withheld GA places are cleared. Audited (`inventory.for_sale.mark_everything_for_sale`), event log and webhook as above. Same response.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"cascadeSeason":{"description":"Sync live season siblings when this event is the season source (default false).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/channel-membership":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"description":"Comma-separated seat ids / labels or GA area ids / names (max 100).","schema":{"type":"string","minLength":1,"maxLength":8000},"in":"query","name":"objects","required":true}],"operationId":"getApiEventsByEventIdChannel-membership","tags":["events"],"summary":"Do objects belong to my inventory channel? (renderer isObjectInChannel)","description":"Seats.io `isObjectInChannel` for the holder of a channel key: send the key(s) in `x-channel-key` (comma-separated for several) and get `{ objects: { [ref]: true | false }, unknown: [ref] }` — `true` when the object belongs to the channel of one of those keys. Only channels whose key you send are answered (public objects and other partitions are `false`), so buyer tokens can ask too. One indexed read; not cached (`private, no-store`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/channel-objects":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdChannel-objects","tags":["events"],"summary":"Objects of my inventory channels (renderer channel-only selection)","description":"Every seat id and GA area id in the channels of the keys sent in `x-channel-key` (comma-separated for several). The renderer reads it once when `objectWithoutChannelSelectable: false` (seats.io `channels` without `NO_CHANNEL`) to keep public objects unselectable. Only channels whose key you send are answered, so buyer tokens can ask too; without a valid key both lists are empty. Two indexed reads; not cached (`private, no-store`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/inventory/channels":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdInventoryChannels","tags":["events"],"summary":"List inventory channels (ops)","description":"Returns id, name, color, and keyPrefix — never the full channelKey secret. Not the same as GET /channels (salesChannel order limits).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdInventoryChannels","tags":["events"],"summary":"Create inventory channel","description":"Creates a channelKey partition. Full `channelKey` secret is returned **once** here; subsequent GET list only returns keyPrefix. Rebuilds Redis channel-keys memo.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":16,"type":"string"},"channelKey":{"minLength":8,"maxLength":64,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":16,"type":"string"},"channelKey":{"minLength":8,"maxLength":64,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":16,"type":"string"},"channelKey":{"minLength":8,"maxLength":64,"type":"string"}}}}}},"responses":{"200":{}}},"put":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdInventoryChannels","tags":["events"],"summary":"Replace all inventory channel assignments atomically","description":"Declarative replacement in one transaction: each listed channel gets exactly the listed seats, GA areas and `areaPlaces`; every other seat/GA area of the event becomes public and unlisted channels keep no areaPlaces. An object listed under two channels is rejected (400). Holds on objects that became more restricted are released after COMMIT; one map-cache invalidate and one `channel` inventory SSE. `assignments: []` makes the whole event public. Response `{ updatedSeats, updatedGaAreas, releasedSeatHolds, clearedGaHoldAreas }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["assignments"],"properties":{"assignments":{"maxItems":64,"type":"array","items":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["assignments"],"properties":{"assignments":{"maxItems":64,"type":"array","items":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}},"text/plain":{"schema":{"type":"object","required":["assignments"],"properties":{"assignments":{"maxItems":64,"type":"array","items":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/inventory/channels/{channelKeyId}":{"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"channelKeyId","required":true}],"operationId":"deleteApiEventsByEventIdInventoryChannelsByChannelKeyId","tags":["events"],"summary":"Delete inventory channel","description":"Reassigns members to public, deletes the channel, rebuilds memo, invalidates map cache once.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"channelKeyId","required":true}],"operationId":"patchApiEventsByEventIdInventoryChannelsByChannelKeyId","tags":["events"],"summary":"Rename or recolor an inventory channel","description":"Updates `name` and/or `color` (#RRGGBB). The secret key, members and areaPlaces are unchanged. Response `{ channel: { id, name, color, keyPrefix, createdAt } }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/inventory/channels/definitions":{"put":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdInventoryChannelsDefinitions","tags":["events"],"summary":"Replace all inventory channels (definitions and assignments) atomically","description":"Seats.io `channels.replace` in **one SQL transaction**: the event's channels become exactly the listed ones. A channel with `id` (or an existing `channelKey`) is kept and renamed / recoloured / re-keyed; any other entry creates a channel; channels not listed are deleted. Every seat and GA area gets the channel it is listed under or becomes public, and each channel's `areaPlaces` is replaced. An object under two channels is rejected (400); an unknown `id` is 404; a `channelKey` used by another channel is 409. Holds on objects that became more restricted are released after COMMIT; the channel-key memo is rebuilt, one map-cache invalidate and one `channel` inventory SSE follow. `channels: []` deletes every channel. Response `{ channels: [{ id, name, color, keyPrefix, created, channelKey? }], deletedChannels, updatedSeats, updatedGaAreas, releasedSeatHolds, clearedGaHoldAreas }` — `channelKey` only for created channels whose secret was generated.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["channels"],"properties":{"channels":{"maxItems":64,"type":"array","items":{"type":"object","required":["name"],"properties":{"id":{"format":"uuid","description":"Existing channel to keep (else matched by channelKey, else created).","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Channel secret; a new channel without one gets a generated secret, returned once.","type":"string"},"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":7,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["channels"],"properties":{"channels":{"maxItems":64,"type":"array","items":{"type":"object","required":["name"],"properties":{"id":{"format":"uuid","description":"Existing channel to keep (else matched by channelKey, else created).","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Channel secret; a new channel without one gets a generated secret, returned once.","type":"string"},"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":7,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}},"text/plain":{"schema":{"type":"object","required":["channels"],"properties":{"channels":{"maxItems":64,"type":"array","items":{"type":"object","required":["name"],"properties":{"id":{"format":"uuid","description":"Existing channel to keep (else matched by channelKey, else created).","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Channel secret; a new channel without one gets a generated secret, returned once.","type":"string"},"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"minLength":7,"maxLength":7,"type":"string"},"seatIds":{"maxItems":100000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}}}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/inventory/channels/assign":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdInventoryChannelsAssign","tags":["events"],"summary":"Assign seats/GA to inventory channel","description":"Batch-set `channel_key_id` (null = public). Releases holds when becoming more restricted. Optional `areaPlaces` merges GA place quotas onto the channel (requires channelKeyId). Invalidates map cache once and publishes inventory SSE (`kind: inventory`, `type: channel`). Viewers with a channel mask should refetch GET /seats. Optional `cascadeSeason` syncs live season siblings when this event is the source (skip past). Does not change salesChannel order limits.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["channelKeyId"],"properties":{"channelKeyId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"seatIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"gaAreaIds":{"maxItems":10000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"areaPlaces":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100000,"type":"integer"}]}}},"cascadeSeason":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/season-status":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdSeason-status","tags":["seasons"],"summary":"Season membership and status overrides of an event","description":"Response `{ eventId, season: { id, name, sourceEventId } | null, isSource, forSalePropagated, overriddenObjects: string[] }` (`overriddenObjects` = seat labels whose status this event overrides).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/actions/override-season-status":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsOverride-season-status","tags":["seasons"],"summary":"Override the season status of objects in an event (OVERRIDE_SEASON_STATUS)","description":"Requires **Idempotency-Key**. The objects of the path event (a non-source season member) stop following the season status. Idempotent per object. Response `{ ok, eventId, seasonId, overridden, added, released, objects? }`.\n\n**No overbooking.** Every object keeps exactly one authority per event (its seat row and Redis key), so an override only changes which (event, object) pairs the season fan-outs touch: season booking (`seasonId` on reserve), season status changes (`seasonId` on change-object-status), season inventory sync and the season booking view skip overridden pairs. Overriding never changes the status: a seat booked for the season stays booked in the event. `release: true` frees the objects in this event only. Unsold objects go through the administrative transition engine (held seats and accepted orders are refused, 409 `inventory_conflict`). Sold objects are refused with 409 `season_booked` (`objects: [{ objectId, label, orderId, seasonTicket }]`) unless `allowBookedRelease: true` is sent explicitly; then only seats of a season (multi-event) order are given back for this event, under the seat row lock: the reservation and the order item of this event are cancelled (`season_status_override`), the seat becomes available and its Redis confirmed marker is cleared; the order stays confirmed for its other events (refunds are up to you). The event's own sales are never released here (409 `event_sale`: cancel the order). Nothing changes on any refusal. The season source event cannot override (409 `season_source_event`); events outside a season get 409 `not_season_member`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"release":{"description":"Also free the objects in this event (max 64).","type":"boolean"},"allowBookedRelease":{"description":"With release: allow freeing booked / resale objects (season tickets).","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"release":{"description":"Also free the objects in this event (max 64).","type":"boolean"},"allowBookedRelease":{"description":"With release: allow freeing booked / resale objects (season tickets).","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"release":{"description":"Also free the objects in this event (max 64).","type":"boolean"},"allowBookedRelease":{"description":"With release: allow freeing booked / resale objects (season tickets).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/use-season-status":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsUse-season-status","tags":["seasons"],"summary":"Make objects follow the season status again (USE_SEASON_STATUS)","description":"Requires **Idempotency-Key**. Removes the overrides of the listed objects in one transaction (event override rows and seat rows of the event and of the season source locked in id order). Allowed only when the event and the season agree commercially for every object: both free, or both sold in the same order; otherwise **409 `season_status_conflict`** with `objects: [{ objectId, label, reason: held | event_sold | season_sold | retired | missing }]` and nothing changes. Then the season source state of those objects is copied (for sale when `forSalePropagated`, channel, custom statuses; custom statuses the event set on its own are cleared). Objects that were not overridden are listed in `notOverridden`. Response `{ ok, eventId, seasonId, reverted, notOverridden, syncSummary }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":1000,"description":"Seat labels or seat UUIDs of the path event.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/events/{eventId}":{"patch":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"patchApiSeasonsBySeasonIdEventsByEventId","tags":["seasons"],"summary":"Season member settings (forSalePropagated)","description":"seats.io `forSalePropagated`: while true (default) season sync and the source's for-sale cascade copy the season source's for-sale flags onto this member. A direct for-sale change on the member (POST /events/{eventId}/inventory/for-sale/apply, or category availability in PATCH /events/{eventId}) sets it to false so the member keeps its own configuration. Setting it back to `true` copies the source's for-sale flags right away (`syncSummary`). Channels and statuses are not affected. The source event itself is refused (409 `season_source_event`). While false, seats of this member that are not for sale but have an active resale listing can be held and confirmed (resale of season tickets); setting it back to true ends that at once.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["forSalePropagated"],"properties":{"forSalePropagated":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["forSalePropagated"],"properties":{"forSalePropagated":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["forSalePropagated"],"properties":{"forSalePropagated":{"type":"boolean"}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"deleteApiSeasonsBySeasonIdEventsByEventId","tags":["seasons"],"summary":"Detach event from season","description":"If the event was the source, clears sourceEventId (sync blocked until reset).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons":{"get":{"operationId":"getApiSeasons","tags":["seasons"],"summary":"List seasons","description":"Lists multi-event seasons for the tenant. Seasons group events for inventory ops sync (NFS + channelKey). Distinct from salesChannel and from inventory channelKey partitions.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiSeasons","tags":["seasons"],"summary":"Create season","description":"Creates an empty season (optionally bound to `chartId` and seeded with `sourceEventId`), or — seats.io `seasons.create` — the season **and its events** in one transaction with `eventKeys` (one event per key, named \"{season} · {key}\") or `numberOfEvents` (N events \"{season} · 1…N\") on `chartId`. Every created event joins the season, the first becomes its source; all or nothing (402 plan event limit, 409 key already used). Response adds `events: [{ id, key, name }]`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"eventKeys":{"minItems":1,"maxItems":50,"description":"Create one season event per key (requires chartId).","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","type":"integer"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"eventKeys":{"minItems":1,"maxItems":50,"description":"Create one season event per key (requires chartId).","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","type":"integer"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"eventKeys":{"minItems":1,"maxItems":50,"description":"Create one season event per key (requires chartId).","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"Create this many season events (requires chartId).","type":"integer"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value.","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"getApiSeasonsBySeasonId","tags":["seasons"],"summary":"Get season detail","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"patchApiSeasonsBySeasonId","tags":["seasons"],"summary":"Update season","description":"Rename, set / rename / clear the partner `key` (409 `season_key_taken`), set sourceEventId (must be a member), or switch `bookingsPropagated`. Clearing source blocks sync until reset.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set or rename the key; `null` or `\"\"` clears it. 409 `season_key_taken`. Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value. Changing it never moves existing bookings; it decides where later season bookings go.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set or rename the key; `null` or `\"\"` clears it. 409 `season_key_taken`. Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value. Changing it never moves existing bookings; it decides where later season bookings go.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Set or rename the key; `null` or `\"\"` clears it. 409 `season_key_taken`. Partner key: unique per workspace, 1–100 URL-safe characters (letters, digits, `_ . ~ -`, starting with a letter or digit), never UUID-shaped. Every season route accepts the key in place of the id (path, body and query `seasonId`), like event and chart keys.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"sourceEventId":{"anyOf":[{"minLength":36,"maxLength":36,"type":"string"},{"type":"null"}]},"bookingsPropagated":{"description":"seats.io seasons whose bookings do not propagate. `true` (default): a season booking (`seasonId` on hold / reserve, confirm and change-object-status) locks the object in every live member event. `false`: the season source event is the season's own inventory — season bookings lock the object there only and the other member events keep selling it on their own. For-sale configuration, channels and category sync are not affected. Season reports default their `seasonBookingsPropagated` parameter to this value. Changing it never moves existing bookings; it decides where later season bookings go.","type":"boolean"}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"deleteApiSeasonsBySeasonId","tags":["seasons"],"summary":"Delete season","description":"Removes the season grouping; does not delete events or wipe inventory.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdEvents","tags":["seasons"],"summary":"Attach event to season","description":"Rejects chart mismatch. Default sync materialises NFS + channelKey + object_statuses from the source onto the new member (unique labels). Past members are skipped. Inheritance is cascade-on-write + skip-past on attach — not a second table. GET /events/:id/seats does not join seasons.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"setAsSource":{"type":"boolean"},"sync":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"setAsSource":{"type":"boolean"},"sync":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"setAsSource":{"type":"boolean"},"sync":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/actions/create-events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdActionsCreate-events","tags":["seasons"],"summary":"Create events in a season","description":"seats.io `seasons.createEvents`: creates new events on the **season chart** and adds them to this season in **one transaction** (all or nothing, under the season row lock). Send exactly one of `eventKeys`, `numberOfEvents` or `events` (`{ key?, name?, date? }`), at most 50. A season without a source event gets the first new event as source; otherwise the new events receive the season inventory (for-sale state, channels, holds) from the source after COMMIT (`syncSummary`). 402 plan event limit, 409 key already used, 409 `season_has_no_chart` when the season has no chart yet. Response `{ events: [{ id, key, name, date }], syncSummary }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventKeys":{"minItems":1,"maxItems":50,"description":"One new event per key (named \"{season} · {key}\").","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","type":"integer"}]},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"minLength":1,"maxLength":100,"type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"format":"date-time","description":"Event date (ISO 8601); default now.","type":"string"}}}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"eventKeys":{"minItems":1,"maxItems":50,"description":"One new event per key (named \"{season} · {key}\").","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","type":"integer"}]},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"minLength":1,"maxLength":100,"type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"format":"date-time","description":"Event date (ISO 8601); default now.","type":"string"}}}}}}},"text/plain":{"schema":{"type":"object","properties":{"eventKeys":{"minItems":1,"maxItems":50,"description":"One new event per key (named \"{season} · {key}\").","type":"array","items":{"minLength":1,"maxLength":100,"type":"string"}},"numberOfEvents":{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"description":"This many new events, numbered after the current members (\"{season} · N\").","type":"integer"}]},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"minLength":1,"maxLength":100,"type":"string"},"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"format":"date-time","description":"Event date (ISO 8601); default now.","type":"string"}}}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/actions/add-events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdActionsAdd-events","tags":["seasons"],"summary":"Add several events to a season (atomic)","description":"Up to 50 events in **one transaction** (all or nothing, under the season row lock): 404 `events_not_found`, 409 `already_in_other_season` or `chart_mismatch` list the offending `eventIds` and nothing is added. Events already in this season are ignored. New members are synced from the source after COMMIT (`sync`, default true). Response `{ added, eventIds (all members), syncSummary }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"sync":{"description":"Sync inventory from the source into the new members (default true).","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"sync":{"description":"Sync inventory from the source into the new members (default true).","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"sync":{"description":"Sync inventory from the source into the new members (default true).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/actions/remove-events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdActionsRemove-events","tags":["seasons"],"summary":"Remove several events from a season (atomic)","description":"One transaction: the events leave the season and its partial seasons; when the source leaves, `sourceEventId` is cleared (`sourceCleared: true`, sync waits for a new source). Events not in the season are ignored; inventory and sales are untouched. Response `{ removed, eventIds (remaining members), sourceCleared }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/inventory/sync":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdInventorySync","tags":["seasons"],"summary":"Sync season inventory from source","description":"Materialises for_sale + inventory channels (same secrets) + assignments + object_statuses into live (non-isInThePast) child events. Label match requires uniqueness on both sides. Caps at 50 members per call when eventIds omitted. Past members are skipped (inheritance, not a second table). Does not touch GET /seats hot path tables beyond child event rows.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","properties":{"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/seats":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"getApiSeasonsBySeasonIdSeats","tags":["seasons"],"summary":"Season booking view (source geometry + intersection status)","description":"Returns the source event seat map with visualStatus sold when a label is not free on every live (non-past) member. Source NFS/channel is the season mask (no second table). lockAliases maps member seat ids to source ids for SSE fan-in. Does not join seasons from GET /events/:id/seats. See docs/SEATSIO_PARITY.md.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"getApiSeasonsBySeasonIdPartial-seasons","tags":["seasons"],"summary":"List partial seasons of a season","description":"Response `{ partialSeasons: [{ id, seasonId, key, name, eventIds, createdAt }] }`. Booking or releasing with `seasonId` set to a partial season id on POST /api/events/:eventId/actions/change-object-status fans out to the partial season members only, atomically, exactly like a full season.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdPartial-seasons","tags":["seasons"],"summary":"Create a partial season","description":"Every event must already be a member of the parent season (400 `not_season_member` lists the others). Max 100 partial seasons per season. 409 `partial_season_key_taken`. Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it. Booking or releasing with `seasonId` set to a partial season id on POST /api/events/:eventId/actions/change-object-status fans out to the partial season members only, atomically, exactly like a full season.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]},"eventIds":{"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"partialSeasonId","required":true}],"operationId":"getApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonId","tags":["seasons"],"summary":"Get one partial season","description":"Response `{ partialSeason: { id, seasonId, key, name, eventIds, createdAt } }`; 404 when it is not a partial season of this season. Booking or releasing with `seasonId` set to a partial season id on POST /api/events/:eventId/actions/change-object-status fans out to the partial season members only, atomically, exactly like a full season.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"partialSeasonId","required":true}],"operationId":"patchApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonId","tags":["seasons"],"summary":"Rename a partial season or set its key","description":"Changes `name` and/or `key` (a key sets or renames it; `null` or `\"\"` clears it; 409 `partial_season_key_taken`). Response `{ partialSeason }`. Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"key":{"description":"Partner `key`: unique within the season, 1–100 URL-safe characters, never UUID-shaped; usable in place of the partial season id in every partial season path (`/seasons/{seasonIdOrKey}/partial-seasons/{partialSeasonIdOrKey}`), and as body `seasonId` of booking and status changes when no season and no other partial season of the workspace uses it.","anyOf":[{"maxLength":100,"type":"string"},{"type":"null"}]}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"partialSeasonId","required":true}],"operationId":"deleteApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonId","tags":["seasons"],"summary":"Delete a partial season","description":"Removes the grouping only; event inventory and sales are untouched.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/actions/add-events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"partialSeasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdActionsAdd-events","tags":["seasons"],"summary":"Add events to a partial season (bulk, all-or-nothing)","description":"Up to 50 events per call; each must be a member of the parent season. Already-present events are ignored.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/seasons/{seasonId}/partial-seasons/{partialSeasonId}/actions/remove-events":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"seasonId","required":true},{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"partialSeasonId","required":true}],"operationId":"postApiSeasonsBySeasonIdPartial-seasonsByPartialSeasonIdActionsRemove-events","tags":["seasons"],"summary":"Remove events from a partial season (bulk)","description":"Up to 50 events per call. Events detached from the parent season leave its partial seasons automatically.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["eventIds"],"properties":{"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/events/actions/change-object-status":{"post":{"parameters":[],"operationId":"postApiEventsActionsChange-object-status","tags":["object-status"],"summary":"Atomically change object statuses across events (all-or-nothing batch)","description":"Applies several status changes — different events, statuses, hold tokens and orders — **all-or-nothing in one SQL transaction**. Either every change is applied or none is; there is never a partial result to reconcile.\n\n**Body**: `statusChanges[]` (1–100; at most 512 objects overall), each with exactly one of `eventId` / `eventKey`, `objects` (seat UUID or label; GA area id or name with `quantity`; optional per-object `ticketType` for bookings), `status` (or `type: RELEASE`) and the same optional fields as POST /events/{eventId}/actions/change-object-status: `orderId`, `holdToken` (book seats held by a buyer hold token), `keepExtraData`, `resalePriceCents`, `resaleListingId`, `allowedPreviousStatuses` / `rejectedPreviousStatuses`, `channelKeys` / `ignoreChannels`, `ticketBuyerId`. One **Idempotency-Key** covers the whole batch: a replay returns the stored response, a retry after a crash reuses the same booking orders.\n\n**Consistency**: row locks are taken in a deterministic order — booking orders by id, then every seat of every change by (eventId, seatId), then GA areas by (eventId, areaId) — so concurrent batches on overlapping seats serialize instead of deadlocking (a database-detected deadlock is retried transparently). Buyer holds are honoured exactly as for a single-event change: a seat held by a buyer (Redis hold lock, read under the seat row lock) is never booked unless its `holdToken` is supplied, hold sessions are frozen before the transaction, and GA places are first reserved in the same structure buyers hold from, so capacity can never be exceeded.\n\n**Not atomic, refused with 400 `not_atomic`**: administrative GA holds (`reservedByToken`) and GA releases by hold token (they live in Redis only). An object or hold token may appear in only one change (400 `duplicate_object` / `duplicate_hold_token`).\n\n**Responses**: 200 `{ results: [{ index, eventId, outcome: \"applied\", objects, gaAreas }] }` in request order. On any failure nothing is written and the status is that of the first failing change (400, 404, 409), with `{ error, message, failed, results: [{ index, eventId, outcome: \"failed\" | \"rolled_back\", status?, error?, message?, objects?, labels? }] }` — every invalid change is reported at once when the request itself is invalid; a conflict found under the row locks reports the change that hit it.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["statusChanges"],"properties":{"statusChanges":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["objects"],"properties":{"eventId":{"minLength":36,"maxLength":36,"description":"Event UUID (exactly one of eventId / eventKey).","type":"string"},"eventKey":{"minLength":1,"maxLength":100,"description":"Event key (your event id; exactly one of eventId / eventKey).","type":"string"},"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"description":"`booked`, `free`, `resale`, `retired` or a custom status (required unless type is RELEASE).","type":"string"},"type":{"description":"`RELEASE` frees the objects (same as status `free`).","anyOf":[{"const":"CHANGE_STATUS_TO","type":"string"},{"const":"RELEASE","type":"string"}]},"orderId":{"maxLength":255,"description":"Existing booking order to act on (UUID).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"description":"Books seats held by this buyer hold token (status `booked`).","type":"string"},"keepExtraData":{"type":"boolean"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["statusChanges"],"properties":{"statusChanges":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["objects"],"properties":{"eventId":{"minLength":36,"maxLength":36,"description":"Event UUID (exactly one of eventId / eventKey).","type":"string"},"eventKey":{"minLength":1,"maxLength":100,"description":"Event key (your event id; exactly one of eventId / eventKey).","type":"string"},"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"description":"`booked`, `free`, `resale`, `retired` or a custom status (required unless type is RELEASE).","type":"string"},"type":{"description":"`RELEASE` frees the objects (same as status `free`).","anyOf":[{"const":"CHANGE_STATUS_TO","type":"string"},{"const":"RELEASE","type":"string"}]},"orderId":{"maxLength":255,"description":"Existing booking order to act on (UUID).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"description":"Books seats held by this buyer hold token (status `booked`).","type":"string"},"keepExtraData":{"type":"boolean"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}},"text/plain":{"schema":{"type":"object","required":["statusChanges"],"properties":{"statusChanges":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","required":["objects"],"properties":{"eventId":{"minLength":36,"maxLength":36,"description":"Event UUID (exactly one of eventId / eventKey).","type":"string"},"eventKey":{"minLength":1,"maxLength":100,"description":"Event key (your event id; exactly one of eventId / eventKey).","type":"string"},"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"description":"`booked`, `free`, `resale`, `retired` or a custom status (required unless type is RELEASE).","type":"string"},"type":{"description":"`RELEASE` frees the objects (same as status `free`).","anyOf":[{"const":"CHANGE_STATUS_TO","type":"string"},{"const":"RELEASE","type":"string"}]},"orderId":{"maxLength":255,"description":"Existing booking order to act on (UUID).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"description":"Books seats held by this buyer hold token (status `booked`).","type":"string"},"keepExtraData":{"type":"boolean"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}}}},"responses":{"200":{}}}},"/api/listing-types":{"get":{"operationId":"getApiListing-types","tags":["listings"],"summary":"List the listing types of the workspace","description":"Listing types give listings their look (`backgroundColor`, `iconColor`, `icon`, shown on seats, in the legend and on the listing price) and their semantics (`kind`: primary | resale, used by the viewer listingType filter). The reserved key `resale` styles every listing created without a type (seats.io convention).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiListing-types","tags":["listings"],"summary":"Create a listing type","description":"Key unique per workspace (409 `listing_type_exists`). Partner keys need the `events_write` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["key","name"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"description":"Icon name (FontAwesome 4.7 names, as seats.io).","type":"string"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["key","name"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"description":"Icon name (FontAwesome 4.7 names, as seats.io).","type":"string"},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["key","name"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"description":"Icon name (FontAwesome 4.7 names, as seats.io).","type":"string"},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/listing-types/{key}":{"patch":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$"},"in":"path","name":"key","required":true}],"operationId":"patchApiListing-typesByKey","tags":["listings"],"summary":"Update a listing type","description":"Viewers of events with active listings of this type receive the new look at once (SSE).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"type":"string"},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"type":"string"},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"name":{"minLength":1,"maxLength":120,"type":"string"},"kind":{"description":"`resale`: objects resold by their owner (listingType filter `resale`); `primary`: first-hand inventory sold as a listing.","anyOf":[{"const":"primary","type":"string"},{"const":"resale","type":"string"}]},"backgroundColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"iconColor":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","description":"CSS hex color `#rrggbb`.","type":"string"},{"type":"null"}]},"icon":{"anyOf":[{"minLength":1,"maxLength":48,"type":"string"},{"type":"null"}]}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$"},"in":"path","name":"key","required":true}],"operationId":"deleteApiListing-typesByKey","tags":["listings"],"summary":"Delete a listing type","description":"Refused while active listings use it (409 `listing_type_in_use`); closed listings keep their history without the type.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/listings":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"schema":{"anyOf":[{"const":"active","type":"string"},{"const":"sold","type":"string"},{"const":"withdrawn","type":"string"}]},"in":"query","name":"status","required":false},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$"},"in":"query","name":"type","required":false},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"integer"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false}],"operationId":"getApiEventsByEventIdListings","tags":["listings"],"summary":"List the listings of an event","description":"Creation order; page with `cursor` = the previous `nextCursor`. Filter by `status` and `type` (`resale` includes untyped listings). A listing groups objects of one event sold together (`sellAsWhole`, default true) or one by one, with a price per object (`priceCents`, or per object in `objects[].priceCents`) or a `totalPriceCents` (sold as a whole only). An object belongs to at most one active listing (409 `listing_object_conflict`). **Holds:** holding any seat of a sold-as-whole listing holds all of its seats in the same atomic hold (POST /seats/reserve returns every seat); general-admission places of such a listing are held exactly as listed; confirming part of it is refused (409 `listing_incomplete`); best available never picks its seats. A confirmed order that sells every seat of an active listing closes it as `sold` with the order id. **With live holds**, withdrawing, or changing `objects` or `sellAsWhole`, is refused with 409 `listing_has_holds` (`heldObjects`) until the holds end (confirmed, released or expired); price, type and metadata changes apply at once. Viewers receive listings in the compact state (`listings` block) and live over SSE. Changes are written to the event log (`event.listings_updated`) and delivered as the `event.listings_updated` webhook.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdListings","tags":["listings"],"summary":"Create a listing","description":"Objects by id or label (max 50); key unique per event (409 `listing_key_taken`; a UUID is generated when omitted). Partner keys need the `bookings` scope. A listing groups objects of one event sold together (`sellAsWhole`, default true) or one by one, with a price per object (`priceCents`, or per object in `objects[].priceCents`) or a `totalPriceCents` (sold as a whole only). An object belongs to at most one active listing (409 `listing_object_conflict`). **Holds:** holding any seat of a sold-as-whole listing holds all of its seats in the same atomic hold (POST /seats/reserve returns every seat); general-admission places of such a listing are held exactly as listed; confirming part of it is refused (409 `listing_incomplete`); best available never picks its seats. A confirmed order that sells every seat of an active listing closes it as `sold` with the order id. **With live holds**, withdrawing, or changing `objects` or `sellAsWhole`, is refused with 409 `listing_has_holds` (`heldObjects`) until the holds end (confirmed, released or expired); price, type and metadata changes apply at once. Viewers receive listings in the compact state (`listings` block) and live over SSE. Changes are written to the event log (`event.listings_updated`) and delivered as the `event.listings_updated` webhook.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["objects"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["objects"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["objects"],"properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/listings/{listingId}":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"description":"Listing id or key.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"listingId","required":true}],"operationId":"getApiEventsByEventIdListingsByListingId","tags":["listings"],"summary":"Read a listing","description":"By id or key.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"description":"Listing id or key.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"listingId","required":true}],"operationId":"patchApiEventsByEventIdListingsByListingId","tags":["listings"],"summary":"Update an active listing","description":"Closed listings return 409 `listing_closed`. A listing groups objects of one event sold together (`sellAsWhole`, default true) or one by one, with a price per object (`priceCents`, or per object in `objects[].priceCents`) or a `totalPriceCents` (sold as a whole only). An object belongs to at most one active listing (409 `listing_object_conflict`). **Holds:** holding any seat of a sold-as-whole listing holds all of its seats in the same atomic hold (POST /seats/reserve returns every seat); general-admission places of such a listing are held exactly as listed; confirming part of it is refused (409 `listing_incomplete`); best available never picks its seats. A confirmed order that sells every seat of an active listing closes it as `sold` with the order id. **With live holds**, withdrawing, or changing `objects` or `sellAsWhole`, is refused with 409 `listing_has_holds` (`heldObjects`) until the holds end (confirmed, released or expired); price, type and metadata changes apply at once. Viewers receive listings in the compact state (`listings` block) and live over SSE. Changes are written to the event log (`event.listings_updated`) and delivered as the `event.listings_updated` webhook.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"type":{"anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"type":{"anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"type":{"anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"objects":{"minItems":1,"maxItems":50,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},{"additionalProperties":false,"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"description":"Seat id or label, or general-admission area id or name (with quantity).","type":"string"},"quantity":{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"description":"General-admission places (required for an area).","type":"integer"}]},"priceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]}}}]}},"sellAsWhole":{"type":"boolean"},"priceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"description":"Partner data (≤ 4 KiB JSON), returned as is.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/listings/{listingId}/actions/withdraw":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"description":"Listing id or key.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"listingId","required":true}],"operationId":"postApiEventsByEventIdListingsByListingIdActionsWithdraw","tags":["listings"],"summary":"Withdraw a listing","description":"The objects leave the listing (they keep their status; resale objects stay booked). Refused with 409 `listing_has_holds` while any object is held: retry after the holds end. Closed listings return 409 `listing_closed`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/listings/{listingId}/actions/mark-sold":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"description":"Listing id or key.","schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"listingId","required":true}],"operationId":"postApiEventsByEventIdListingsByListingIdActionsMark-sold","tags":["listings"],"summary":"Mark a listing as sold","description":"For listings sold outside SeatsChart (for example a resale settled by the partner). Listings sold through a confirmed order close by themselves.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"orderId":{"minLength":1,"maxLength":255,"description":"Partner order reference.","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"orderId":{"minLength":1,"maxLength":255,"description":"Partner order reference.","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"orderId":{"minLength":1,"maxLength":255,"description":"Partner order reference.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/object-statuses":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdObject-statuses","tags":["object-status"],"summary":"List object status overlays for an event","description":"Ops overlay (custom / resale / extraData). Distinct from Redis holds. See docs/SEATSIO_PARITY.md.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/actions/change-object-status":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsChange-object-status","tags":["object-status"],"summary":"Change object status (booked / free / resale / retired / custom)","description":"`type: RELEASE` frees objects; otherwise `status` is required. Optional `resalePriceCents` is stored on extraData. Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/book":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsBook","tags":["object-status"],"summary":"Create a durable administrative booking on unheld inventory (or on a hold token)","description":"Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/hold":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsHold","tags":["object-status"],"summary":"Hold general admission places administratively (issues a GA hold token)","description":"Shortcut for `status: \"reservedByToken\"`; only GA quantities are accepted. Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/retire":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsRetire","tags":["object-status"],"summary":"Retire objects (not for sale, history kept)","description":"Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/release":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsRelease","tags":["object-status"],"summary":"Release unsold objects; sales require explicit order cancellation","description":"Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/put-up-for-resale":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsPut-up-for-resale","tags":["object-status"],"summary":"Put booked objects up for resale","description":"Resale retains the durable owner and sold status. Optional `resaleListingId` groups the objects into one listing (extraData.resaleListingId). Optional `resalePriceCents` is stored on extraData and merged into GET /seats on the next cache rebuild. Optional `listing` also creates a server-side listing of the objects (type, sellAsWhole, price; see the listings tag): viewers then show and sell it as one listing. Administrative status change on the path event. One SQL transaction per event; seats are locked in id order and every precondition is evaluated under those row locks, so a concurrent hold, purchase or other change cannot slip in between the check and the write. Requires **Idempotency-Key**; a replay returns the stored response and a retry after a crash reuses the same durable booking order.\n\n**Objects** (max 64 per call, per event): seat UUID or seat label (labels are resolved per event; a label that matches several seats returns 409 `ambiguous_label`; unknown references return 404 `objects_not_found`). General admission areas are addressed by UUID or name **with `quantity`** (1–10000):\n- `booked`: the places are first reserved in the same Redis structure buyers hold from (capacity minus not-for-sale places), then `sold_count` is incremented in the transaction with a `sold_count + quantity <= capacity` guard; the durable receipt belongs to the administrative booking order (cancel it with POST /orders/{orderId}/cancel).\n- `reservedByToken` (or POST …/actions/hold, GA only): holds the places atomically against capacity minus withheld, sold and held places and issues a **GA hold token** (`hold: { holdToken, holdUserId, holdExpiresAt }`), confirmable with POST /ga/confirm (`holdToken`, `userId` = `holdUserId`). Optional `userId` (customer identity of the hold; one hold per user and area, 409 `hold_exists`) and `holdExpiresInMinutes` (1–120). 409 `insufficient_capacity`.\n- `RELEASE` / `free` without `holdToken`: releases places of **administrative** bookings (newest receipts first, optionally only those of `orderId`), recording a release receipt per consumed booking and decrementing `sold_count` under the GA row lock (409 `insufficient_booked_places` with `available`). Buyer purchases are never released here.\n- `RELEASE` with `holdToken` (GA hold token): releases that many places held by the token (409 `insufficient_held_places`, `hold_token_conflict`).\nEvery GA operation is idempotent per Idempotency-Key (deterministic receipts/holds/markers), and capacity can never be exceeded nor `sold_count` go below zero. Response `gaAreas: { <areaId>: { areaId, label, quantity, soldCount, status, heldCount?, releasedFrom? } }`.\n\n**`allowedPreviousStatuses` / `rejectedPreviousStatuses`**: `free`, `booked`, `resale`, `reservedByToken`, `retired` or a custom status. On violation nothing is written and 409 `illegal_previous_status` lists every offending object with its current status. For GA quantities the previous status follows from the operation: `free` for book/hold, `booked` for a release, `reservedByToken` for a release by `holdToken`.\n\n**`holdToken`**: with status `booked`, books seats held by that hold token. The hold session is frozen first, so the buyer cannot confirm the same seats concurrently; seats held by another token or not held by this one return 409 `hold_token_conflict`. Remaining seats of the session stay held. With `RELEASE` it releases GA places (see above).\n\n**`ticketType`** (per object, `{ objectId, ticketType }`, status `booked` only): ticket type id recorded on the new booking (order item and reservation, like a confirmed sale) and returned per object; it shows up in reports by ticket type. Other statuses return 400. **`keepExtraData`**: keeps the stored extraData; per-object `extraData` sent with it is merged on top (without it, the object's extraData is replaced by the one sent, or cleared).\n\n**`ticketBuyerId`** (opaque, ≤ 256 chars, never an internal user id) is recorded on new bookings, object statuses, status history and GA hold tokens; query it with GET /events/{eventId}/objects?ticketBuyerId=….\n\n**`channelKeys`** (channel secret keys or channel ids) restricts the change to public objects and objects of the named channels (409 `channel_forbidden` otherwise). Without `channelKeys`, or with `ignoreChannels: true`, channel partitions are ignored (backward-compatible default).\n\n**`events`** (max 50, must include the path event): applies the same change to each event of the tenant, labels resolved per event. By default each event is atomic on its own (no cross-event atomicity): response 200 when every event succeeded, otherwise 207 with `results[]` (`{ eventId, ok, objects | status, error, message }`), `succeeded` and `failed`. Replaying the same Idempotency-Key returns the stored per-event outcome; retry failed events (listed with their error) with a new key. **`atomic: true`** makes the whole call all-or-nothing in one SQL transaction (same engine as POST /events/actions/change-object-status; at most 512 objects across the events): 200 with `atomic: true` when everything was applied, otherwise the status of the first failing event and `results[]` with `outcome: failed` (with `error`, `message`) or `rolled_back` per event — nothing was written. GA administrative holds cannot be atomic (400 `not_atomic`).\n\n**`seasonId`** (season or partial season id; XOR with `events`): fans out by unique label to every live member in one transaction (cap 50 members, 64 seats overall). Preconditions and channelKeys apply to every member seat.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"object","required":["objectId"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"quantity":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"ticketType":{"minLength":1,"maxLength":64,"description":"Ticket type id of the booking (status `booked` only), stored on the order item like a confirmed sale.","type":"string"}}}]}},"status":{"minLength":1,"maxLength":64,"type":"string"},"type":{"minLength":1,"maxLength":32,"type":"string"},"orderId":{"maxLength":255,"type":"string"},"keepExtraData":{"type":"boolean"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"resalePriceCents":{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},"resaleListingId":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Resale listing: stored on extraData.resaleListingId of every object; the renderer can select a listing as one group (resaleListings.selectAsGroup).","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"allowedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"rejectedPreviousStatuses":{"minItems":1,"maxItems":16,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"channelKeys":{"maxItems":64,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"type":"boolean"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"atomic":{"description":"With `events`: all-or-nothing across the events (one SQL transaction). Default false (each event on its own).","type":"boolean"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"GA holds only: customer identity of the hold (POST /ga/confirm must send the same userId).","type":"string"},"holdExpiresInMinutes":{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"GA holds only: hold lifetime (default: seat hold TTL).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"listing":{"additionalProperties":false,"description":"put-up-for-resale only: also creates a server-side listing of these objects (GET /events/{eventId}/listings). Checked before the status change (409 `listing_object_conflict`, `listing_key_taken`); the response carries `listing`.","type":"object","properties":{"key":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","description":"Listing key (default: `resaleListingId`, else a generated UUID).","type":"string"},"type":{"description":"Listing type key; omitted = untyped (resale).","anyOf":[{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~:-]{0,63}$","type":"string"},{"type":"null"}]},"sellAsWhole":{"description":"Default true: the objects are held and sold together.","type":"boolean"},"priceCents":{"description":"Price per object (default: `resalePriceCents`).","anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"totalPriceCents":{"anyOf":[{"minimum":0,"maximum":99999999,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":99999999,"type":"integer"}]},{"type":"null"}]},"partnerMetadata":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/update-extra-data":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsUpdate-extra-data","tags":["object-status"],"summary":"Update extraData of objects without changing their status","description":"Sets (default) or merges (`merge: true`) `extraData` on up to 64 seats addressed by UUID or label; `extraData: null` clears it. Status, ownership, holds and for-sale flags are never touched. Runs under the same seat row locks as status changes, so it cannot interleave with a concurrent transition. Max 16 KiB of JSON per object. Requires **Idempotency-Key** (replays return the stored response). Every update is written to the audit log (`object.extra_data.update`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"type":"object","required":["objectId","extraData"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"merge":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"type":"object","required":["objectId","extraData"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"merge":{"type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["objects"],"properties":{"objects":{"minItems":1,"maxItems":64,"type":"array","items":{"type":"object","required":["objectId","extraData"],"properties":{"objectId":{"minLength":1,"maxLength":128,"type":"string"},"extraData":{"anyOf":[{"type":"object","patternProperties":{"^(.*)$":{}}},{"type":"null"}]}}}},"merge":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/hold-tokens":{"post":{"parameters":[],"operationId":"postApiHold-tokens","tags":["seats"],"summary":"Create an empty hold-token session","description":"Seats.io-style session start. Attach seats via reserve using this token later, or renew TTL with POST /hold-tokens/:token. Optional `ticketBuyerId` (opaque partner buyer id) is stored on the session and inherited by its confirmation. Optional `expiresInMinutes` (1–120, seats.io `holdTokens.create(expiresInMinutes)`) sets the session lifetime instead of the workspace hold period; buyer tokens get 403.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"expiresInMinutes":{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","type":"integer"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"expiresInMinutes":{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","type":"integer"}]}}}},"text/plain":{"schema":{"type":"object","required":["eventId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"expiresInMinutes":{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"description":"Session lifetime in minutes (1–120; default: the workspace hold period). API keys and dashboard sessions only.","type":"integer"}]}}}}}},"responses":{"200":{}}}},"/api/hold-tokens/{token}":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"token","required":true}],"operationId":"postApiHold-tokensByToken","tags":["seats"],"summary":"Change hold-token expiration (max 2h from creation)","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["expiresInMinutes"],"properties":{"expiresInMinutes":{"minimum":0,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":120,"type":"integer"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["expiresInMinutes"],"properties":{"expiresInMinutes":{"minimum":0,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":120,"type":"integer"}]}}}},"text/plain":{"schema":{"type":"object","required":["expiresInMinutes"],"properties":{"expiresInMinutes":{"minimum":0,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":120,"type":"integer"}]}}}}}},"responses":{"200":{}}},"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"token","required":true}],"operationId":"getApiHold-tokensByToken","tags":["seats"],"summary":"Validate a hold-token session","description":"Returns seatIds and expiry for a live hold token. Does not mutate Redis locks or the hold:token JSON. 410 when expired, 404 when the token is not in this tenant.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/sales-rules":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdSales-rules","tags":["events"],"summary":"Read the sales rules of an event","description":"Sales rules of an event, enforced atomically in the same Redis call as the seat hold (no overbooking of a quota, lot or buyer limit under any concurrency): `quotas` (meia-entrada: ticket types counted together, `maxShare` of the event capacity and/or `maxCount`), `lotPlans` (lots switch by `quantity` sold+held or at `untilDate`; `prices` per ticket type for catalog modes), `buyerLimit` (`maxTickets` per ticketBuyerId — e.g. a CPF bound in the viewer token — holds included) and `pricingSource` (`partner` | `synced_catalog` | `seatschart_catalog`). Refusals: 409 `quota_exceeded`, `buyer_limit_exceeded`, `lot_sold_out`; 403 `ticket_buyer_required`. Counters are released on hold expiry, release, refused confirmation, failed order and order cancellation. Saving rebuilds the counters from the event orders on next use.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"put":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdSales-rules","tags":["events"],"summary":"Replace the sales rules of an event (quotas, lots, buyer limit, pricing source)","description":"Sales rules of an event, enforced atomically in the same Redis call as the seat hold (no overbooking of a quota, lot or buyer limit under any concurrency): `quotas` (meia-entrada: ticket types counted together, `maxShare` of the event capacity and/or `maxCount`), `lotPlans` (lots switch by `quantity` sold+held or at `untilDate`; `prices` per ticket type for catalog modes), `buyerLimit` (`maxTickets` per ticketBuyerId — e.g. a CPF bound in the viewer token — holds included) and `pricingSource` (`partner` | `synced_catalog` | `seatschart_catalog`). Refusals: 409 `quota_exceeded`, `buyer_limit_exceeded`, `lot_sold_out`; 403 `ticket_buyer_required`. Counters are released on hold expiry, release, refused confirmation, failed order and order cancellation. Saving rebuilds the counters from the event orders on next use. Partner keys need the `events_write` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"description":"Event override of the workspace pricing source; null = workspace setting.","anyOf":[{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},{"type":"null"}]},"quotas":{"maxItems":8,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","label","ticketTypes"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":16,"description":"Ticket type ids counted by this quota (e.g. [\"meia\"]).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"}},"maxShare":{"exclusiveMinimum":0,"maximum":1,"description":"Share of the event capacity (0.4 = 40%, Lei 12.933).","type":"number"},"maxCount":{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","type":"integer"}]}}}},"lotPlans":{"maxItems":16,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","lots"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"categories":{"maxItems":64,"description":"Seat categories sold through this plan; omit for the plan that sells every other category.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"lots":{"minItems":1,"maxItems":12,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"quantity":{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","type":"integer"}]},"untilDate":{"format":"date-time","description":"The lot closes at this instant.","type":"string"},"prices":{"description":"Catalog modes: price per ticket type id in cents while this lot is active.","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}},"buyerLimit":{"anyOf":[{"additionalProperties":false,"type":"object","required":["maxTickets"],"properties":{"maxTickets":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"requireBuyerId":{"description":"Refuse holds without a ticketBuyerId.","type":"boolean"}}},{"type":"null"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"description":"Event override of the workspace pricing source; null = workspace setting.","anyOf":[{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},{"type":"null"}]},"quotas":{"maxItems":8,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","label","ticketTypes"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":16,"description":"Ticket type ids counted by this quota (e.g. [\"meia\"]).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"}},"maxShare":{"exclusiveMinimum":0,"maximum":1,"description":"Share of the event capacity (0.4 = 40%, Lei 12.933).","type":"number"},"maxCount":{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","type":"integer"}]}}}},"lotPlans":{"maxItems":16,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","lots"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"categories":{"maxItems":64,"description":"Seat categories sold through this plan; omit for the plan that sells every other category.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"lots":{"minItems":1,"maxItems":12,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"quantity":{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","type":"integer"}]},"untilDate":{"format":"date-time","description":"The lot closes at this instant.","type":"string"},"prices":{"description":"Catalog modes: price per ticket type id in cents while this lot is active.","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}},"buyerLimit":{"anyOf":[{"additionalProperties":false,"type":"object","required":["maxTickets"],"properties":{"maxTickets":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"requireBuyerId":{"description":"Refuse holds without a ticketBuyerId.","type":"boolean"}}},{"type":"null"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"description":"Event override of the workspace pricing source; null = workspace setting.","anyOf":[{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},{"type":"null"}]},"quotas":{"maxItems":8,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","label","ticketTypes"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"label":{"minLength":1,"maxLength":64,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":16,"description":"Ticket type ids counted by this quota (e.g. [\"meia\"]).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"}},"maxShare":{"exclusiveMinimum":0,"maximum":1,"description":"Share of the event capacity (0.4 = 40%, Lei 12.933).","type":"number"},"maxCount":{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":1000000,"description":"Absolute ticket count; with maxShare the lower limit applies.","type":"integer"}]}}}},"lotPlans":{"maxItems":16,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","lots"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"categories":{"maxItems":64,"description":"Seat categories sold through this plan; omit for the plan that sells every other category.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"lots":{"minItems":1,"maxItems":12,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"quantity":{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000000,"description":"Tickets sold (and held) in this lot before the next opens.","type":"integer"}]},"untilDate":{"format":"date-time","description":"The lot closes at this instant.","type":"string"},"prices":{"description":"Catalog modes: price per ticket type id in cents while this lot is active.","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}},"buyerLimit":{"anyOf":[{"additionalProperties":false,"type":"object","required":["maxTickets"],"properties":{"maxTickets":{"minimum":1,"maximum":10000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":10000,"type":"integer"}]},"requireBuyerId":{"description":"Refuse holds without a ticketBuyerId.","type":"boolean"}}},{"type":"null"}]}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/sales-counters":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdSales-counters","tags":["events"],"summary":"Live sales counters (quotas, lots) of an event","description":"Sold and held tickets per quota and lot, read from the atomic Redis counters (expired holds are pruned first). Partner keys need the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/sales-counters/actions/rebuild":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdSales-countersActionsRebuild","tags":["events"],"summary":"Rebuild the sold counters of an event from its orders","description":"Recounts sold tickets per ticket type, lot and buyer from the event order items (PostgreSQL); live holds are kept. Use after manual data repairs.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/pricing":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdPricing","tags":["events"],"summary":"Read the synced price table of an event","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"put":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"putApiEventsByEventIdPricing","tags":["events"],"summary":"Push the synced price table of an event (synced catalog mode)","description":"Categories × ticket types with prices in cents, optional fees and per-lot prices. Displayed by the renderer for events whose pricing source is `synced_catalog` and used to validate confirmations (409 `price_mismatch`) and quotes. Cached per event by content hash: no database read on reserve/confirm. Sets the event currency when it has none (409 `currency_mismatch` when it differs). Partner keys need the `events_write` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["currency","categories"],"properties":{"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"allFeesIncluded":{"type":"boolean"},"categories":{"minItems":1,"maxItems":128,"type":"array","items":{"additionalProperties":false,"type":"object","required":["category","ticketTypes"],"properties":{"category":{"minLength":1,"maxLength":128,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":32,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"priceCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"feeCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"lotPrices":{"description":"Price per lot id (lots of the event sales rules).","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["currency","categories"],"properties":{"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"allFeesIncluded":{"type":"boolean"},"categories":{"minItems":1,"maxItems":128,"type":"array","items":{"additionalProperties":false,"type":"object","required":["category","ticketTypes"],"properties":{"category":{"minLength":1,"maxLength":128,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":32,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"priceCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"feeCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"lotPrices":{"description":"Price per lot id (lots of the event sales rules).","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["currency","categories"],"properties":{"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"allFeesIncluded":{"type":"boolean"},"categories":{"minItems":1,"maxItems":128,"type":"array","items":{"additionalProperties":false,"type":"object","required":["category","ticketTypes"],"properties":{"category":{"minLength":1,"maxLength":128,"type":"string"},"ticketTypes":{"minItems":1,"maxItems":32,"type":"array","items":{"additionalProperties":false,"type":"object","required":["id","name","priceCents"],"properties":{"id":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$","type":"string"},"name":{"minLength":1,"maxLength":64,"type":"string"},"priceCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"description":{"maxLength":240,"type":"string"},"feeCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"lotPrices":{"description":"Price per lot id (lots of the event sales rules).","type":"object","patternProperties":{"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}}}}}}},"responses":{"200":{}}}},"/api/events/{eventId}/actions/move-to-new-chart-copy":{"post":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"postApiEventsByEventIdActionsMove-to-new-chart-copy","tags":["events"],"summary":"Move the event to a new copy of its chart","description":"Copies the event's current chart (published seat/GA templates, layout and draft) into a new published chart and repoints the event to it in one transaction, so the event's map can be edited without affecting other events. Seats, holds, sales and orders are never touched (seat ids are unchanged). Refuses with 409 `structural_mismatch` (`orphanedObjects`: labels) when a sold, held or pending seat — or a GA area with sales — has no counterpart in the chart, and with 409 `chart_not_published` when there is nothing to copy. Counts against the plan chart limit. Response `{ eventId, previousChartId, chartId, chartName }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/events/bulk":{"post":{"parameters":[],"operationId":"postApiEventsBulk","tags":["events"],"summary":"Create up to 50 events for one chart","description":"Creates every event (with its cloned seat/GA inventory and published geometry) in **one transaction**: either all events exist afterwards or none. The plan event limit is checked for the whole batch (402). Optional `seasonId` attaches each created event afterwards (like POST /events); `seasonId: null` in an item means that attachment failed. A chart with error-level validation issues is refused with 409 `chart_invalid` (`issues`, `index`) and nothing is created. Every item accepts the creation-time configuration of POST /events (`key`, `tableBookingConfig`, `categoryOverrides`, `channels`, `forSaleConfig`), applied in the same transaction; a rejected item returns its error with `index` and nothing is created. Keys must be unique in the batch and the workspace (409 `event_key_taken`). Emits one `event.created` webhook for the batch. Response `{ events: [{ id, key, name, date, chartId, clonedSeats, clonedGaAreas, seasonId, categoryOverrides, tableBookingConfig, channels, forSale }] }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chartId","events"],"properties":{"chartId":{"minLength":36,"maxLength":36,"type":"string"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}}}},"seasonId":{"minLength":36,"maxLength":36,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["chartId","events"],"properties":{"chartId":{"minLength":36,"maxLength":36,"type":"string"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}}}},"seasonId":{"minLength":36,"maxLength":36,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["chartId","events"],"properties":{"chartId":{"minLength":36,"maxLength":36,"type":"string"},"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":512,"type":"string"},"date":{"minLength":10,"maxLength":64,"type":"string"},"categories":{"description":"Event-only categories (seats.io event `categories`; same rules as PATCH /events/{eventId}).","anyOf":[{"maxItems":128,"type":"array","items":{"type":"object","required":["key","label","color"],"properties":{"key":{"minLength":1,"maxLength":64,"description":"Category key, unique in the event; referenced by objectCategories.","type":"string"},"label":{"minLength":1,"maxLength":64,"description":"Display name; also the seat category value (overrides, price tables, sales rules).","type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","description":"#RRGGBB.","type":"string"},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}},{"type":"null"}]},"objectCategories":{"description":"Object label (seat label or GA area name) → category key (event category key, chart category id or name); same rules as PATCH /events/{eventId}.","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"type":"string"}}},{"type":"null"}]},"key":{"pattern":"^[A-Za-z0-9][A-Za-z0-9_.~-]{0,99}$","minLength":1,"maxLength":100,"description":"Optional partner key, unique per workspace, accepted wherever an event id is (URL-safe, not UUID-shaped).","type":"string"},"tableBookingConfig":{"description":"Per-event table booking mode (same as PATCH /events/{eventId}).","anyOf":[{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"const":"all_by_seat","type":"string"},{"const":"all_by_table","type":"string"},{"const":"custom","type":"string"}]},"tables":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"const":"by_seat","type":"string"},{"const":"by_table","type":"string"}]}}}}},{"type":"null"}]},"categoryOverrides":{"description":"Per-event category overrides (same semantics as PATCH /events/{eventId}).","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"object","properties":{"priceCents":{"anyOf":[{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},{"type":"null"}]},"tint":{"anyOf":[{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},{"type":"null"}]},"forSale":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}}}},{"type":"null"}]},"channels":{"maxItems":64,"description":"Inventory channels created with the event; `channelKey` secrets are returned once.","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":128,"type":"string"},"color":{"pattern":"^#[0-9a-fA-F]{6}$","type":"string"},"channelKey":{"minLength":8,"maxLength":64,"description":"Secret channel key; generated when omitted.","type":"string"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}},"forSaleConfig":{"type":"object","required":["forSale"],"properties":{"forSale":{"description":"`false`: the listed objects/categories are not for sale and `areaPlaces` are withheld places. `true`: only the listed objects/categories are for sale and `areaPlaces` are the places left for sale.","type":"boolean"},"objects":{"maxItems":10000,"description":"Seat labels or UUIDs and GA area names or UUIDs of the new event.","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"categories":{"maxItems":256,"type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"areaPlaces":{"description":"GA area name or UUID → number of places.","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":100000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":100000,"type":"integer"}]}}}}}}}},"seasonId":{"minLength":36,"maxLength":36,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/viewer-tokens":{"post":{"parameters":[],"operationId":"postApiViewer-tokens","tags":["embed"],"summary":"Mint a short-lived buyer (viewer) token with the partner API key","description":"Server-to-server only (`x-api-key`, `holds` scope). A dashboard session (Bearer of a workspace member) may also mint one: the web viewer page does so for a signed-in organizer instead of handing the session to the viewer; buyer tokens never can. Returns an HS256 buyer JWT bound to the API key's workspace (`tenant_id` claim) for the viewer `?token=` / `token` prop or `Authorization: Bearer` on consumer routes. Partners never need the platform `JWT_SECRET`. Mint one per buyer session from your server; never expose the API key in the browser. Lifetime 60-3600 seconds (default 900). Buyer tokens cannot mint tokens (403). Optional `ticketBuyerId` binds the token to a ticket buyer: holds made with it carry that id. With the workspace setting **ticket buyer validation** on, `ticketBuyerId` is required (400 `ticket_buyer_id_required`) and must be registered (`POST /ticket-buyers`; 403 `ticket_buyer_not_registered`); buyer holds then refuse a different id (403 `ticket_buyer_mismatch`) and tokens without one (403 `ticket_buyer_required`). Response: `{ token, tokenType: \"Bearer\", expiresInSeconds, expiresAt, ticketBuyerId? }`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"ttlSeconds":{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"ttlSeconds":{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"ttlSeconds":{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":3600,"description":"Token lifetime in seconds (default 900).","type":"integer"}]},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}},"responses":{"200":{}}}},"/api/embed-tokens":{"post":{"parameters":[],"operationId":"postApiEmbed-tokens","tags":["embed"],"summary":"Mint a short-lived designer/manager embed token","description":"HS256 audience `embed`, max TTL 30 minutes. Never put the workspace API secret in the browser. A designer token carries its `mode` (`normal`, `safe` or `readOnly`), which the API enforces on every write: the embedded designer's own `mode` only adapts the UI. Tokens minted without a mode are `normal`. See docs/SEATSIO_PARITY.md.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["purpose"],"properties":{"purpose":{"anyOf":[{"const":"designer","type":"string"},{"const":"manager","type":"string"}]},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"mode":{"description":"Designer tokens only, enforced by the API (default `normal`, full edit). `safe`: saves, publishes and version restores that would delete or relabel published objects, change the booking type of a published table or area, or change the capacity of a published area are refused with 403 `forbidden` / `ERR_DESIGNER_SAFE_MODE` (`reason`, `objectIds`). `readOnly`: every write is refused with 403 `forbidden` / `ERR_DESIGNER_READ_ONLY`.","anyOf":[{"const":"normal","type":"string"},{"const":"safe","type":"string"},{"const":"readOnly","type":"string"}]},"safeModeOptions":{"additionalProperties":false,"description":"Exceptions to safe mode (seats.io `safeModeOptions`); only with `mode: \"safe\"`.","type":"object","properties":{"allowDeletingObjects":{"description":"Allow deleting published objects (default false).","type":"boolean"},"allowEditingAreaCapacity":{"description":"Allow changing the capacity of published areas (default false).","type":"boolean"}}},"ttlSeconds":{"minimum":60,"maximum":1800,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":1800,"type":"integer"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["purpose"],"properties":{"purpose":{"anyOf":[{"const":"designer","type":"string"},{"const":"manager","type":"string"}]},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"mode":{"description":"Designer tokens only, enforced by the API (default `normal`, full edit). `safe`: saves, publishes and version restores that would delete or relabel published objects, change the booking type of a published table or area, or change the capacity of a published area are refused with 403 `forbidden` / `ERR_DESIGNER_SAFE_MODE` (`reason`, `objectIds`). `readOnly`: every write is refused with 403 `forbidden` / `ERR_DESIGNER_READ_ONLY`.","anyOf":[{"const":"normal","type":"string"},{"const":"safe","type":"string"},{"const":"readOnly","type":"string"}]},"safeModeOptions":{"additionalProperties":false,"description":"Exceptions to safe mode (seats.io `safeModeOptions`); only with `mode: \"safe\"`.","type":"object","properties":{"allowDeletingObjects":{"description":"Allow deleting published objects (default false).","type":"boolean"},"allowEditingAreaCapacity":{"description":"Allow changing the capacity of published areas (default false).","type":"boolean"}}},"ttlSeconds":{"minimum":60,"maximum":1800,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":1800,"type":"integer"}]}}}},"text/plain":{"schema":{"type":"object","required":["purpose"],"properties":{"purpose":{"anyOf":[{"const":"designer","type":"string"},{"const":"manager","type":"string"}]},"chartId":{"minLength":36,"maxLength":36,"type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"mode":{"description":"Designer tokens only, enforced by the API (default `normal`, full edit). `safe`: saves, publishes and version restores that would delete or relabel published objects, change the booking type of a published table or area, or change the capacity of a published area are refused with 403 `forbidden` / `ERR_DESIGNER_SAFE_MODE` (`reason`, `objectIds`). `readOnly`: every write is refused with 403 `forbidden` / `ERR_DESIGNER_READ_ONLY`.","anyOf":[{"const":"normal","type":"string"},{"const":"safe","type":"string"},{"const":"readOnly","type":"string"}]},"safeModeOptions":{"additionalProperties":false,"description":"Exceptions to safe mode (seats.io `safeModeOptions`); only with `mode: \"safe\"`.","type":"object","properties":{"allowDeletingObjects":{"description":"Allow deleting published objects (default false).","type":"boolean"},"allowEditingAreaCapacity":{"description":"Allow changing the capacity of published areas (default false).","type":"boolean"}}},"ttlSeconds":{"minimum":60,"maximum":1800,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":60,"maximum":1800,"type":"integer"}]}}}}}},"responses":{"200":{}}}},"/api/seats/group":{"get":{"parameters":[{"schema":{"type":"string","minLength":36},"in":"query","name":"eventIds","required":true}],"operationId":"getApiSeatsGroup","tags":["seats"],"summary":"Event-group availability intersection by label","description":"A label is selectable only when it is free on every non-past member (cap 50). Does not join seasons. See docs/SEATSIO_PARITY.md.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/seats":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdSeats","tags":["events"],"summary":"List seats for an event","description":"Returns the seat map (`dbStatus` from Postgres, `visualStatus` merged with live Redis locks) only when the event belongs to the authenticated tenant. Cache-aside key `event:{tenantId}:{eventId}:map` skips the Postgres scan on hits (`X-Seat-Map-Cache: hit|miss`) and merges live TTL locks into each build. Builds are shared per (tenant, event, view, channel cohort) and reused while the inventory version is unchanged, for at most 1 s (`X-Seat-Map-Microcache: hit|shared|miss|bypass`). Responses carry a weak `ETag` (If-None-Match → 304) and are br/gzip-encoded when accepted. Optional header **`x-channel-key`** authorizes an inventory partition (not salesChannel); several comma-separated keys (max 16) authorize every listed channel (renderer `channelKeys`), on reads, holds and best available alike. Buyers receive a masked payload (no channelKeyId); restricted seats without a matching key are non-bookable. channelKey→id resolution uses Redis memo `event:…:channel-keys` (no PG on cache hit when memo is warm). Optional `resalePriceCents` is merged at cache build from the overlay table — the seats SELECT list is unchanged. `forSale` is the effective flag, the same one holds, confirmations and the viewer state use: a seat that is not for sale on its own but is in an active resale listing of a season member whose for-sale configuration does not follow the season (`forSalePropagated: false`) is reported with `forSale: true` and the additive marker `forSaleViaResale: true` (absent on every other seat). Both are decided at cache build from the event row and the cached listing context (no extra query on cache hits); listing and season for-sale changes invalidate the cached map.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/manifest":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdManifest","tags":["events"],"summary":"Published geometry manifest","description":"Manifest of the published map (`mapRevision`, `geometryHash` and `geometryUrl` of the geometry served by `GET /api/events/{eventId}/geometry/{hash}`, `seatCount`, `gaCount`) plus the event `currency`, the static `sales` description when the event has sales rules or an external price source, and `testWorkspace: true` in test workspaces. ETag covers the revision, the currency and the sales rules; `If-None-Match` returns 304. 404 `not_found` for an unknown event, 409 `geometry_not_published` before the first publish. `Cache-Control: private, no-cache`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/geometry/{hash}":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"in":"path","name":"hash","required":true}],"operationId":"getApiEventsByEventIdGeometryByHash","tags":["events"],"summary":"Published geometry (content-addressed)","description":"Immutable map geometry addressed by the SHA-256 hash from the manifest. Brotli or gzip when `Accept-Encoding` allows (`Content-Encoding` set), else identity JSON. Strong ETag per hash and coding; `If-None-Match` returns 304. `Cache-Control: private, max-age=31536000, immutable`; 404 `not_found` (never cached) when the hash is not a published geometry of this event. Authenticated on every request.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/state":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdState","tags":["events"],"summary":"Compact visual state (protocol 5)","description":"Buyer availability for the caller cohort (`x-channel-key`). Coalesced per API process: concurrent readers share one authority read, and a value is reused only while the event inventory version is unchanged and it is younger than `STATE_MICROCACHE_MAX_AGE_MS` (at most 1 s). Weak ETag = stateToken + epoch.sequence; `If-None-Match` returns 304. Gzip-encoded when accepted. `Cache-Control: private, no-cache`. Visual state is bounded-stale; reserve/confirm remain authoritative.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/events/{eventId}/seat-views":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"views":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["imageUrl"],"properties":{"imageUrl":{"type":"string"},"panoUrl":{"type":"string"},"kind":{"anyOf":[{"const":"photo","type":"string"},{"const":"equirect","type":"string"}]}}}}}},"required":["eventId","views"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"views":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["imageUrl"],"properties":{"imageUrl":{"type":"string"},"panoUrl":{"type":"string"},"kind":{"anyOf":[{"const":"photo","type":"string"},{"const":"equirect","type":"string"}]}}}}}},"required":["eventId","views"]}},"text/plain":{"schema":{"type":"object","properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"views":{"type":"object","patternProperties":{"^(.*)$":{"type":"object","required":["imageUrl"],"properties":{"imageUrl":{"type":"string"},"panoUrl":{"type":"string"},"kind":{"anyOf":[{"const":"photo","type":"string"},{"const":"equirect","type":"string"}]}}}}}},"required":["eventId","views"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiEventsByEventIdSeat-views","tags":["events"],"summary":"List view-from-seat image and optional 360 URLs","description":"Buyer-readable. Returns CDN URLs from layoutMeta.seatViews (imageUrl, optional panoUrl, optional kind photo|equirect) — never data URLs, never joined into GET /seats, never written to the seat-map cache. Hybrid auth.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/events/{eventId}/actions/scan-chart":{"post":{"operationId":"postApiEventsByEventIdActionsScan-chart","tags":["charts"],"deprecated":true,"summary":"Retired: scan an event chart image (410)","description":"Retired legacy AI scan route: always answers 410 `scan_route_retired` without calling any AI provider. Use `POST /api/extract-seats` with a multipart image.","parameters":[{"schema":{"type":"string"},"in":"path","name":"eventId","required":true}],"responses":{"200":{}}}},"/api/charts/{chartId}/actions/scan-chart":{"post":{"operationId":"postApiChartsByChartIdActionsScan-chart","tags":["charts"],"deprecated":true,"summary":"Retired: scan a chart image (410)","description":"Retired legacy AI scan route: always answers 410 `scan_route_retired` without calling any AI provider. Use `POST /api/extract-seats` with a multipart image.","parameters":[{"schema":{"type":"string"},"in":"path","name":"chartId","required":true}],"responses":{"200":{}}}},"/api/extract-seats":{"post":{"parameters":[{"schema":{"minimum":4,"maximum":200,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":4,"maximum":200,"type":"number"}]},"in":"query","name":"seat_diameter","required":false},{"schema":{"anyOf":[{"const":"1","type":"string"},{"const":"3","type":"string"},{"const":"5","type":"string"},{"const":"7","type":"string"}]},"in":"query","name":"closing_kernel","required":false},{"schema":{"minimum":0,"maximum":255,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":0,"maximum":255,"type":"number"}]},"in":"query","name":"canny_low","required":false},{"schema":{"exclusiveMinimum":0,"maximum":255,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"exclusiveMinimum":0,"maximum":255,"type":"number"}]},"in":"query","name":"canny_high","required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"width":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"height":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"seats":{"maxItems":20000,"type":"array","items":{"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How sure the scanner is that this is a seat (auto and examples modes).","type":"number"},"doubtful":{"description":"Present (true) on seats to review before keeping them.","type":"boolean"}}}},"mode":{"anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"},{"const":"calibration","type":"string"},{"const":"profile","type":"string"}]},"legibility":{"description":"`ok`; `low_resolution`: the seats are drawn too small to be read one by one; `no_individual_seats`: the plan shows sections or blocks, not seats. When not `ok`, every seat is `doubtful` and the count must not be trusted.","anyOf":[{"const":"ok","type":"string"},{"const":"low_resolution","type":"string"},{"const":"no_individual_seats","type":"string"}]},"review":{"minimum":0,"description":"How many seats are flagged `doubtful`.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"How many seats are flagged `doubtful`.","type":"integer"}]},"vector":{"description":"Only when a `vector` file was sent: `used` when its drawn geometry checked the seats, `unusable` when it held none (a scanned page, no seat-sized shapes, over the work budget) and the image alone was scanned.","anyOf":[{"const":"used","type":"string"},{"const":"unusable","type":"string"}]},"suggested":{"maxItems":20000,"description":"Auto and examples modes: places where a seat was probably missed (a gap in a row, the end of a row, a mark that looks like the clicked seats but scored below the threshold), best first. They are not part of `seats`; the designer offers them in the review, where one key adds one. Omitted when there are none.","type":"array","items":{"type":"object","required":["x","y","confidence"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How likely a seat is there.","type":"number"}}}}},"required":["width","height","seats"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"width":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"height":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"seats":{"maxItems":20000,"type":"array","items":{"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How sure the scanner is that this is a seat (auto and examples modes).","type":"number"},"doubtful":{"description":"Present (true) on seats to review before keeping them.","type":"boolean"}}}},"mode":{"anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"},{"const":"calibration","type":"string"},{"const":"profile","type":"string"}]},"legibility":{"description":"`ok`; `low_resolution`: the seats are drawn too small to be read one by one; `no_individual_seats`: the plan shows sections or blocks, not seats. When not `ok`, every seat is `doubtful` and the count must not be trusted.","anyOf":[{"const":"ok","type":"string"},{"const":"low_resolution","type":"string"},{"const":"no_individual_seats","type":"string"}]},"review":{"minimum":0,"description":"How many seats are flagged `doubtful`.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"How many seats are flagged `doubtful`.","type":"integer"}]},"vector":{"description":"Only when a `vector` file was sent: `used` when its drawn geometry checked the seats, `unusable` when it held none (a scanned page, no seat-sized shapes, over the work budget) and the image alone was scanned.","anyOf":[{"const":"used","type":"string"},{"const":"unusable","type":"string"}]},"suggested":{"maxItems":20000,"description":"Auto and examples modes: places where a seat was probably missed (a gap in a row, the end of a row, a mark that looks like the clicked seats but scored below the threshold), best first. They are not part of `seats`; the designer offers them in the review, where one key adds one. Omitted when there are none.","type":"array","items":{"type":"object","required":["x","y","confidence"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How likely a seat is there.","type":"number"}}}}},"required":["width","height","seats"]}},"text/plain":{"schema":{"type":"object","properties":{"width":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"height":{"minimum":1,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"type":"integer"}]},"seats":{"maxItems":20000,"type":"array","items":{"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How sure the scanner is that this is a seat (auto and examples modes).","type":"number"},"doubtful":{"description":"Present (true) on seats to review before keeping them.","type":"boolean"}}}},"mode":{"anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"},{"const":"calibration","type":"string"},{"const":"profile","type":"string"}]},"legibility":{"description":"`ok`; `low_resolution`: the seats are drawn too small to be read one by one; `no_individual_seats`: the plan shows sections or blocks, not seats. When not `ok`, every seat is `doubtful` and the count must not be trusted.","anyOf":[{"const":"ok","type":"string"},{"const":"low_resolution","type":"string"},{"const":"no_individual_seats","type":"string"}]},"review":{"minimum":0,"description":"How many seats are flagged `doubtful`.","anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"description":"How many seats are flagged `doubtful`.","type":"integer"}]},"vector":{"description":"Only when a `vector` file was sent: `used` when its drawn geometry checked the seats, `unusable` when it held none (a scanned page, no seat-sized shapes, over the work budget) and the image alone was scanned.","anyOf":[{"const":"used","type":"string"},{"const":"unusable","type":"string"}]},"suggested":{"maxItems":20000,"description":"Auto and examples modes: places where a seat was probably missed (a gap in a row, the end of a row, a mark that looks like the clicked seats but scored below the threshold), best first. They are not part of `seats`; the designer offers them in the review, where one key adds one. Omitted when there are none.","type":"array","items":{"type":"object","required":["x","y","confidence"],"properties":{"x":{"minimum":0,"type":"number"},"y":{"minimum":0,"type":"number"},"confidence":{"minimum":0,"maximum":1,"description":"How likely a seat is there.","type":"number"}}}}},"required":["width","height","seats"]}}}},"400":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"403":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"413":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"415":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"422":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"502":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"503":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}},"504":{"description":"Error body: `error` code and `message`.","content":{"application/json":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"multipart/form-data":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}},"text/plain":{"schema":{"type":"object","patternProperties":{"^(.*)$":{}}}}}}},"operationId":"postApiExtract-seats","tags":["map-designer"],"summary":"Extract seat candidates from a floor-plan image","description":"Multipart image (PNG/JPEG/WebP, at most 8 MiB and 16 megapixels). Returns seat centres in source-image pixels for review in the map designer, plus the `mode` that produced them; nothing is persisted. Requires CV_SERVICE_URL.\n\nReview (`auto` and `examples`): every seat carries a `confidence` (0-1) and the uncertain ones `doubtful: true`; `review` counts them and the designer shows them for a keep/remove decision instead of keeping them silently. `legibility` is `ok`, `low_resolution` or `no_individual_seats`; when it is not `ok` every seat is doubtful and the count is not reliable. `suggested` lists places where a seat was probably missed (not part of `seats`); the designer offers them in the review. Clicks never remove a seat on weak evidence: a seat they argue against stays, flagged `doubtful`. When the clicked seats (or, without clicks, an illegible plan) are tiny, the scanner reads the plan again upscaled. Vector plans: send the PDF or SVG as `vector` (and `vector_page`) next to its rendered image; the response says whether its geometry was `used`.\n\nModes: `auto` (default) finds the repeated seat glyphs on its own and infers size, rotation and threshold; `examples` uses 1-24 clicked seats (`examples`): a click on a missed seat adds that look, then only detections consistent with the clicked seats (their look, size and spacing, or the same drawn vector shape) stay, plus seats of another look that the scanner is confident about; every clicked seat is kept; `edges` is the legacy contour mode tuned by the `seat_diameter`/`closing_kernel` query parameters.\n\nRefinements for `auto` and `examples`: `negatives` (points the user marked as not a seat after a scan) remove that detection and its look-alikes, or the whole style when most of it looks alike; `region` limits the scan to a rectangle.\n\n`calibration` (sample patches) and `profile` (the mapa1-mapa6 regression benchmark) are kept for backward compatibility and cannot be combined with `mode`, `examples`, `negatives` or `region`. Errors: 422 for invalid parameters or work budgets, 413/415 for oversized or unsupported images (or a `vector` file that is neither PDF nor SVG), 503 when the scanner is busy or not configured, 504 on its 80-second deadline.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["file"],"properties":{"file":{"default":"File","maxSize":8388608,"type":"string","format":"binary"},"calibration":{"description":"Calibration JSON (sample patches, rotations, grouping) for the legacy matcher.","anyOf":[{"maxLength":16384,"type":"string"},{"type":"object","patternProperties":{"^(.*)$":{}}}]},"profile":{"pattern":"^mapa[1-6]$","type":"string"},"mode":{"description":"`auto` (default): discover the seat glyphs and match them, no clicks needed. `examples`: use the seats clicked in `examples`. `edges`: legacy contour mode tuned by `seat_diameter` and `closing_kernel`.","anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"}]},"examples":{"description":"JSON array of 1-24 example seat centres in source-image pixels, e.g. `[{\"x\":412,\"y\":230}]`. Implies `mode=examples`.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"negatives":{"description":"JSON array of 1-24 wrong detections of an earlier scan (\"not a seat\"), in source-image pixels, e.g. `[{\"x\":96,\"y\":40}]`. Works with `auto` and `examples`: the detection under each point and everything that looks like it are removed; when most of a seat style looks like it, that style is dropped and the plan is rescanned without it.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"region":{"description":"JSON object `{\"x\",\"y\",\"width\",\"height\"}` in source-image pixels limiting the scan to that rectangle (leaves out a legend, title or stage text). Works with `auto` and `examples`; example points must lie inside it. Returned seats stay in whole-image coordinates.","anyOf":[{"maxLength":512,"type":"string"},{"additionalProperties":false,"type":"object","required":["x","y","width","height"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"},"width":{"exclusiveMinimum":0,"maximum":16384,"type":"number"},"height":{"exclusiveMinimum":0,"maximum":16384,"type":"number"}}}]},"vector":{"default":"File","maxSize":8388608,"description":"Optional vector source of `file` (a PDF or an SVG, at most 8 MiB): `file` must be that PDF page or SVG rendered whole (any scale; the page box, or the SVG viewBox, maps onto the full image). The scanner reads the drawn geometry (closed paths, rectangles, circles, repeated symbols, text runs) and uses it to check, add and drop seats; a scanned page or one without seat-sized shapes falls back to the image alone (`vector: \"unusable\"`). Works with `auto` and `examples`.","type":"string","format":"binary"},"vector_page":{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","type":"number"}]}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["file"],"properties":{"file":{"default":"File","maxSize":8388608,"type":"string","format":"binary"},"calibration":{"description":"Calibration JSON (sample patches, rotations, grouping) for the legacy matcher.","anyOf":[{"maxLength":16384,"type":"string"},{"type":"object","patternProperties":{"^(.*)$":{}}}]},"profile":{"pattern":"^mapa[1-6]$","type":"string"},"mode":{"description":"`auto` (default): discover the seat glyphs and match them, no clicks needed. `examples`: use the seats clicked in `examples`. `edges`: legacy contour mode tuned by `seat_diameter` and `closing_kernel`.","anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"}]},"examples":{"description":"JSON array of 1-24 example seat centres in source-image pixels, e.g. `[{\"x\":412,\"y\":230}]`. Implies `mode=examples`.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"negatives":{"description":"JSON array of 1-24 wrong detections of an earlier scan (\"not a seat\"), in source-image pixels, e.g. `[{\"x\":96,\"y\":40}]`. Works with `auto` and `examples`: the detection under each point and everything that looks like it are removed; when most of a seat style looks like it, that style is dropped and the plan is rescanned without it.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"region":{"description":"JSON object `{\"x\",\"y\",\"width\",\"height\"}` in source-image pixels limiting the scan to that rectangle (leaves out a legend, title or stage text). Works with `auto` and `examples`; example points must lie inside it. Returned seats stay in whole-image coordinates.","anyOf":[{"maxLength":512,"type":"string"},{"additionalProperties":false,"type":"object","required":["x","y","width","height"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"},"width":{"exclusiveMinimum":0,"maximum":16384,"type":"number"},"height":{"exclusiveMinimum":0,"maximum":16384,"type":"number"}}}]},"vector":{"default":"File","maxSize":8388608,"description":"Optional vector source of `file` (a PDF or an SVG, at most 8 MiB): `file` must be that PDF page or SVG rendered whole (any scale; the page box, or the SVG viewBox, maps onto the full image). The scanner reads the drawn geometry (closed paths, rectangles, circles, repeated symbols, text runs) and uses it to check, add and drop seats; a scanned page or one without seat-sized shapes falls back to the image alone (`vector: \"unusable\"`). Works with `auto` and `examples`.","type":"string","format":"binary"},"vector_page":{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","type":"number"}]}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["file"],"properties":{"file":{"default":"File","maxSize":8388608,"type":"string","format":"binary"},"calibration":{"description":"Calibration JSON (sample patches, rotations, grouping) for the legacy matcher.","anyOf":[{"maxLength":16384,"type":"string"},{"type":"object","patternProperties":{"^(.*)$":{}}}]},"profile":{"pattern":"^mapa[1-6]$","type":"string"},"mode":{"description":"`auto` (default): discover the seat glyphs and match them, no clicks needed. `examples`: use the seats clicked in `examples`. `edges`: legacy contour mode tuned by `seat_diameter` and `closing_kernel`.","anyOf":[{"const":"auto","type":"string"},{"const":"examples","type":"string"},{"const":"edges","type":"string"}]},"examples":{"description":"JSON array of 1-24 example seat centres in source-image pixels, e.g. `[{\"x\":412,\"y\":230}]`. Implies `mode=examples`.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"negatives":{"description":"JSON array of 1-24 wrong detections of an earlier scan (\"not a seat\"), in source-image pixels, e.g. `[{\"x\":96,\"y\":40}]`. Works with `auto` and `examples`: the detection under each point and everything that looks like it are removed; when most of a seat style looks like it, that style is dropped and the plan is rescanned without it.","anyOf":[{"maxLength":4096,"type":"string"},{"minItems":1,"maxItems":24,"type":"array","items":{"additionalProperties":false,"type":"object","required":["x","y"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"}}}}]},"region":{"description":"JSON object `{\"x\",\"y\",\"width\",\"height\"}` in source-image pixels limiting the scan to that rectangle (leaves out a legend, title or stage text). Works with `auto` and `examples`; example points must lie inside it. Returned seats stay in whole-image coordinates.","anyOf":[{"maxLength":512,"type":"string"},{"additionalProperties":false,"type":"object","required":["x","y","width","height"],"properties":{"x":{"minimum":0,"maximum":16384,"type":"number"},"y":{"minimum":0,"maximum":16384,"type":"number"},"width":{"exclusiveMinimum":0,"maximum":16384,"type":"number"},"height":{"exclusiveMinimum":0,"maximum":16384,"type":"number"}}}]},"vector":{"default":"File","maxSize":8388608,"description":"Optional vector source of `file` (a PDF or an SVG, at most 8 MiB): `file` must be that PDF page or SVG rendered whole (any scale; the page box, or the SVG viewBox, maps onto the full image). The scanner reads the drawn geometry (closed paths, rectangles, circles, repeated symbols, text runs) and uses it to check, add and drop seats; a scanned page or one without seat-sized shapes falls back to the image alone (`vector: \"unusable\"`). Works with `auto` and `examples`.","type":"string","format":"binary"},"vector_page":{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":2000,"multipleOf":1,"description":"PDF page (1-based) that `file` shows; default 1.","type":"number"}]}}}}}}}},"/api/events/{eventId}/stream":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"eventId","required":true}],"operationId":"getApiEventsByEventIdStream","tags":["sse"],"summary":"Indexed visual projection stream","description":"Protocol 5: compact bootstrap or exact checkpoint, absolute indexed deltas and shared heartbeat. Geometry is immutable and separate. No periodic snapshots on the wire. Periodic authority reconciliation repairs lost publications and passive expiry. Transport cursor is independent of geometry identity and verified projection digest. Gaps, dirty subscriptions, overflow and incompatible revisions close with resync_required. Reconnect submits mapRevision, stateToken, epoch and sequence; continuity must be proven or a compact snapshot is returned. Visual state is bounded-stale; reserve/confirm remain authoritative.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks":{"get":{"operationId":"getApiWebhooks","tags":["webhooks"],"summary":"List webhooks for the authenticated tenant","description":"Returns all outbound webhook endpoints of the authenticated tenant with their event-type subscription (`eventTypes`, `[\"*\"]` = all) and the outcome of the most recent delivery (`lastDelivery`). Requires the `webhooks` scope for scoped API keys.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiWebhooks","tags":["webhooks"],"summary":"Create a webhook endpoint","description":"Registers an HTTPS callback URL (except `http://localhost` / `127.0.0.1` only for local development). Duplicate URLs per tenant are rejected with `409 Conflict`. `eventTypes` defaults to `[\"*\"]` (every type); unknown types are rejected with `400`. The response includes `signingSecret` exactly once: deliveries to this endpoint carry `x-seatschart-signature: sha256=<hex HMAC-SHA256 of the raw body>` keyed with it, plus `x-seatschart-timestamp` and `x-seatschart-signature-v2: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"t.rawBody\">` (recommended: verify v1 in constant time and reject timestamps more than 5 minutes old), `X-SeatsChart-Event` and `X-SeatsChart-Event-Id`. In production the host is DNS-resolved at registration and refused when any record is loopback, private, link-local, CGNAT or metadata; every send re-resolves and pins a public address and never follows redirects.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","required":["url"],"properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}}}},"responses":{"200":{}}}},"/api/webhooks/event-types":{"get":{"operationId":"getApiWebhooksEvent-types","tags":["webhooks"],"summary":"List subscribable webhook event types","description":"Event types an endpoint can subscribe to through `eventTypes`. `webhook.test` is sent only by `POST /api/webhooks/{id}/test` and cannot be subscribed to. `aliases`: seats.io-named aliases (`{ type, aliasOf }`), opt-in per endpoint: the aliased event is delivered again under the alias name to endpoints that list it explicitly (same payload, `type` = alias); `[\"*\"]` does not include aliases. **Deprecated spellings**: `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated` and `event.table_booking_changed` carry `deprecated: { replacedBy, sunset }` — subscribe to the seats.io name in `replacedBy`; the old spelling is still emitted and is not removed before 2027-06-01.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}/deliveries":{"get":{"parameters":[{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"`pending` also matches deliveries currently being attempted (`processing`).","schema":{"anyOf":[{"const":"delivered","type":"string"},{"const":"failed","type":"string"},{"const":"pending","type":"string"}]},"in":"query","name":"status","required":false},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z.]+$"},"in":"query","name":"eventType","required":false},{"schema":{"anyOf":[{"const":"outbox","type":"string"},{"const":"dispatch","type":"string"},{"const":"test","type":"string"},{"const":"replay","type":"string"}]},"in":"query","name":"source","required":false}],"operationId":"getApiWebhooksByIdDeliveries","tags":["webhooks"],"summary":"List deliveries of a webhook endpoint","description":"Newest first, cursor-paginated (`nextCursor`). Combines the durable SQL outbox (`reservations.confirmed`, retried with exponential backoff) with fire-and-forget dispatches (`holds.*`, `ga.*`, `objects.updated`, …), test events and replays. Entries expose event id, type, status, attempts, HTTP status and a stable error code only — never response bodies, URLs or credentials. Direct-send entries are retained for `WEBHOOK_DELIVERY_RETENTION_DAYS` (default 30).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}/deliveries/{deliveryId}":{"get":{"parameters":[{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliveryId","required":true}],"operationId":"getApiWebhooksByIdDeliveriesByDeliveryId","tags":["webhooks"],"summary":"Get one webhook delivery with its request payload","description":"Same fields as the list plus `payload`: the JSON request body that was sent (identical bytes are re-sent by replay). Remote response bodies are never stored.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}/deliveries/{deliveryId}/replay":{"post":{"parameters":[{"description":"Optional. Repeating a replay with the same key returns the first result instead of sending again.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":false},{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliveryId","required":true}],"operationId":"postApiWebhooksByIdDeliveriesByDeliveryIdReplay","tags":["webhooks"],"summary":"Replay a webhook delivery","description":"Re-sends the identical request body and `X-SeatsChart-Event-Id` of the original delivery, signed with the endpoint’s CURRENT secret (`x-seatschart-signature`), so receivers can deduplicate by event id. One synchronous attempt; the result is recorded as a new `replay` delivery and returned with `200` whether the remote accepted it or not (inspect `delivery.status`). Works for paused endpoints. Rate limited per workspace (`WEBHOOK_MANUAL_SEND_MAX` per `WEBHOOK_MANUAL_SEND_WINDOW_SEC`, default 30/60 s): `429` with `Retry-After`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}/test":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":false},{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"postApiWebhooksByIdTest","tags":["webhooks"],"summary":"Send a signed test event","description":"Sends `{\"eventId\",\"type\":\"webhook.test\",\"occurred_at\",\"tenant_id\",\"test\":true,\"items\":[]}` signed with the endpoint’s current secret, regardless of its event-type subscription or paused state. One synchronous attempt recorded as a `test` delivery; `200` with `delivery.status` `delivered` or `failed` (`httpStatus`, `lastErrorCode`). Shares the replay rate limit (`429` + `Retry-After`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}":{"patch":{"parameters":[{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"patchApiWebhooksById","tags":["webhooks"],"summary":"Update a webhook","description":"Partial update of `url`, `isActive` and/or `eventTypes` (`[\"*\"]` = every type). Subscription changes apply to events emitted afterwards; queued outbox retries keep their original destinations. Only webhooks owned by the authenticated tenant can be modified.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}},"text/plain":{"schema":{"type":"object","properties":{"url":{"minLength":8,"maxLength":2048,"type":"string"},"isActive":{"type":"boolean"},"eventTypes":{"minItems":1,"maxItems":32,"description":"Subscribed event types: `[\"*\"]` (default) for every type, or a list of `reservations.confirmed`, `holds.created`, `holds.released`, `ga.confirmed`, `ga.released`, `objects.updated`, `season.objects.updated`, `hold.expired`, `event.created`, `event.updated`, `event.deleted`, `chart.published`, `lots.switched`, `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.listings_updated`. Seats.io-named aliases (`event.for.sale.config.updated`, `event.channels.updated`, `event.categories.updated`, `event.table.booking.config.updated`) are opt-in: listed explicitly, the endpoint also receives the aliased event under that name (same payload, `type` = the alias); `[\"*\"]` never includes them.","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"deleteApiWebhooksById","tags":["webhooks"],"summary":"Delete a webhook","description":"Removes the registration permanently. Idempotent `404` applies when the webhook id is unknown or belongs to another tenant.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/webhooks/{id}/rotate-secret":{"post":{"parameters":[{"description":"Webhook UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"postApiWebhooksByIdRotate-secret","tags":["webhooks"],"summary":"Rotate a webhook signing secret","description":"Replaces the endpoint HMAC key and returns the new `signingSecret` once. The old key stops working immediately, including for queued retries. Endpoints created before per-endpoint secrets are signed with the deployment `WEBHOOK_SIGNING_SECRET` until rotated.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage":{"get":{"parameters":[{"description":"UTC calendar month `YYYY-MM` (default: current month).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":false}],"operationId":"getApiUsage","tags":["usage"],"summary":"Monthly usage: used objects, charts and events","description":"Sandboxes (`isTest: true`) are metered so their usage can be seen, but never billed and never held to plan limits (`limit: null`). `usedObjects.count` is the number of distinct objects booked in the UTC calendar month (seats.io-style billing unit): every seat that became booked counts once per month however often it is retried, released and re-booked; each sold general-admission place counts as one. Holds, test events and replays never count. Map-only renders (`bringYourOwnBackend`) count every object of the rendered chart once per chart and month (`byEvent` entry with the chart id and `mapOnly: true`). `history` has exactly 12 months ending at `month` (zero-filled); `byEvent` lists the top 100 events of the month. `charts`/`events` are current totals against the plan limits. Dashboard JWT (any role) or an API key with the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage/charts":{"get":{"parameters":[{"description":"UTC calendar month `YYYY-MM` (default: current month).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":false}],"operationId":"getApiUsageCharts","tags":["usage"],"summary":"Monthly used objects per chart and event","description":"Breakdown of `usedObjects` (see GET /usage) by chart, each with its events (most used first). Events are grouped under their current chart; deleted events or events without a chart are listed under `chartId: null`; map-only renders of a chart (`bringYourOwnBackend`) are one entry `{ eventId: <chart id>, mapOnly: true }` under that chart. Read from the metering table (no counters on the booking path). Dashboard JWT (any role) or an API key.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage/events/{eventId}":{"get":{"parameters":[{"schema":{"type":"string","minLength":36,"maxLength":36},"in":"path","name":"eventId","required":true},{"description":"UTC calendar month `YYYY-MM` (default: current month).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":false},{"description":"Page size (1..1000, default 1000).","schema":{"minimum":1,"maximum":1000,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":1000,"description":"Page size (1..1000, default 1000).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` of the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false}],"operationId":"getApiUsageEventsByEventId","tags":["usage"],"summary":"Monthly used objects of one event (paginated)","description":"Total `usedObjects` of the event in the month, `objectCount` (metered objects) and one page of the metered objects (`objectKey`: seat id, or `ga:<receiptId>` with `units` places; `firstUsedAt` with microseconds), newest first. `limit` 1..1000 (default 1000); pass `nextCursor` as `cursor` for the next page (`null` on the last page; `truncated` is true when more pages follow). Keyset pagination over the primary-key range of the event and month. Works for deleted events that were metered. Map-only renders of a chart (`objectKey: map-only`) are listed under the chart id with `mapOnly: true`. Dashboard JWT (any role) or an API key with the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage/events":{"get":{"parameters":[{"description":"UTC calendar month `YYYY-MM` (default: current month).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":false},{"description":"Page size (1..100, default 50).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 50).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` of the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false}],"operationId":"getApiUsageEvents","tags":["usage"],"summary":"Monthly used objects per event (paginated)","description":"Every event with used objects in the month, most used first (ties by event id): `items: [{ eventId, eventKey, eventName, chartId, usedObjects }]` (`eventName: null` for deleted events; map-only renders of a chart: `eventId` = `chartId` = the chart id and `mapOnly: true`), `eventCount` and the month total `usedObjects`. `limit` 1..100 (default 50); pass `nextCursor` as `cursor` for the next page. Unlike `byEvent` of GET /usage (top 100) this lists every event. Closed months are immutable and their per-event totals are cached by the API. Dashboard JWT (any role) or an API key with the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage/history":{"get":{"parameters":[{"description":"First month `YYYY-MM` (default: 11 months before `to`).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"from","required":false},{"description":"Last month `YYYY-MM` (default: current month; not in the future).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"to","required":false}],"operationId":"getApiUsageHistory","tags":["usage"],"summary":"Used objects per month over a range","description":"Monthly used objects from `from` to `to` inclusive (oldest first, zero-filled), at most 120 months (400 otherwise), plus the range total. One grouped read over the primary-key range `(tenant_id, month)`; closed months are immutable and cached by the API. Dashboard JWT (any role) or an API key with the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/usage/workspaces":{"get":{"parameters":[{"description":"UTC calendar month `YYYY-MM` (default: current month).","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":false}],"operationId":"getApiUsageWorkspaces","tags":["usage"],"summary":"Monthly used objects across my workspaces (admin view)","description":"Every workspace the caller administers (role admin), with its plan, status and used objects of the month against the plan limit (`null` for sandboxes, which are metered but never billed), plus the total of the production workspaces (`usedObjects`) and, separately, of the sandboxes (`sandboxUsedObjects`). Dashboard session, or a **workspace admin API key** (the admin it is bound to); other API keys: 403 `ERR_PARTNER_SCOPE`. One grouped read of the metering table.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/embed-origins":{"get":{"operationId":"getApiEmbed-origins","tags":["embed-origins"],"summary":"List allowed embed origins for the workspace","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiEmbed-origins","tags":["embed-origins"],"summary":"Register an allowed embed origin","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["origin"],"properties":{"origin":{"minLength":4,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["origin"],"properties":{"origin":{"minLength":4,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["origin"],"properties":{"origin":{"minLength":4,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/embed-origins/{id}":{"delete":{"parameters":[{"description":"Embed origin UUID (stored lowercase)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"deleteApiEmbed-originsById","tags":["embed-origins"],"summary":"Remove an embed origin","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/billing":{"get":{"operationId":"getApiBilling","tags":["billing"],"summary":"Get billing status and plan usage","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/billing/checkout-session":{"post":{"parameters":[],"operationId":"postApiBillingCheckout-session","tags":["billing"],"summary":"Create Stripe Checkout session for a paid plan","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["planId"],"properties":{"planId":{"minLength":1,"maxLength":32,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["planId"],"properties":{"planId":{"minLength":1,"maxLength":32,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["planId"],"properties":{"planId":{"minLength":1,"maxLength":32,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/billing/confirm-checkout":{"post":{"parameters":[],"operationId":"postApiBillingConfirm-checkout","tags":["billing"],"summary":"Confirm checkout session and sync plan","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sessionId"],"properties":{"sessionId":{"minLength":1,"maxLength":255,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["sessionId"],"properties":{"sessionId":{"minLength":1,"maxLength":255,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["sessionId"],"properties":{"sessionId":{"minLength":1,"maxLength":255,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/billing/portal-session":{"post":{"operationId":"postApiBillingPortal-session","tags":["billing"],"summary":"Create Stripe Customer Portal session","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/workspace":{"get":{"operationId":"getApiWorkspace","tags":["workspace"],"summary":"Get workspace profile for the authenticated tenant","description":"Includes `publicKey` (`pub_…`) for `?workspaceKey=` on the viewer. The public key is not authentication — Bearer JWT or embed token is still required. `kind` is `sandbox` for a sandbox (`isTest: true`, see `/sandboxes`), with its production workspace in `parentWorkspaceId` and its last reset in `lastResetAt`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[],"operationId":"patchApiWorkspace","tags":["workspace"],"summary":"Update workspace display name","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/workspaces":{"post":{"parameters":[],"operationId":"postApiWorkspaces","tags":["workspace"],"summary":"Create an additional workspace","description":"Provisions a free-plan tenant and admin membership for the caller: a dashboard admin session, or a **workspace admin API key** (`POST /api/keys` with `workspaceAdmin: true`; it must be a full-access key), whose bound admin becomes the admin of the new workspace. Other API keys: 403 `ERR_PARTNER_SCOPE`. `isTest: true` creates a test workspace linked to the current production workspace (`parentWorkspaceId`), optionally copying its charts (`copyCharts`). Written to the audit log of the current workspace (`workspace.create` / `workspace.create_test`). Returns a new `publicKey` — not a secret and not a Bearer substitute.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"isTest":{"description":"Create a sandbox of the current workspace (prefer `POST /sandboxes`): never billed (usage shown, not counted), no chart/event caps, `sk_test_` API keys, a Sandbox ribbon for buyers. Permanent.","type":"boolean"},"copyCharts":{"description":"Test workspaces only: copy the current workspace's charts as unpublished drafts (up to 50, archived skipped). Default false.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"isTest":{"description":"Create a sandbox of the current workspace (prefer `POST /sandboxes`): never billed (usage shown, not counted), no chart/event caps, `sk_test_` API keys, a Sandbox ribbon for buyers. Permanent.","type":"boolean"},"copyCharts":{"description":"Test workspaces only: copy the current workspace's charts as unpublished drafts (up to 50, archived skipped). Default false.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"isTest":{"description":"Create a sandbox of the current workspace (prefer `POST /sandboxes`): never billed (usage shown, not counted), no chart/event caps, `sk_test_` API keys, a Sandbox ribbon for buyers. Permanent.","type":"boolean"},"copyCharts":{"description":"Test workspaces only: copy the current workspace's charts as unpublished drafts (up to 50, archived skipped). Default false.","type":"boolean"}}}}}},"responses":{"200":{}}},"get":{"parameters":[{"description":"Case-insensitive substring of the workspace name or id (seats.io `filter`).","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"filter","required":false},{"description":"`active` (seats.io listActive), `inactive` (deactivated; seats.io listInactive) or `all` (default).","schema":{"anyOf":[{"const":"active","type":"string"},{"const":"inactive","type":"string"},{"const":"all","type":"string"}]},"in":"query","name":"status","required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"],"properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}}}}},"required":["items"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"],"properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}}}}},"required":["items"]}},"text/plain":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"],"properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}}}}},"required":["items"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspaces","tags":["workspace"],"summary":"List my workspaces","description":"Every workspace the signed-in dashboard user belongs to, with role, effective plan, subscription status, activation status and `isCurrent`. `?filter=` keeps the workspaces whose name or id contains the text; `?status=active|inactive` keeps the active or the deactivated ones. Dashboard session, or a **workspace admin API key** (`POST /api/keys` with `workspaceAdmin: true`): it lists the workspaces of the admin it is bound to. Other API keys: 403 `ERR_PARTNER_SCOPE`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/workspace/public-key/rotate":{"post":{"operationId":"postApiWorkspacePublic-keyRotate","tags":["workspace"],"summary":"Rotate the public workspace key","description":"Issues a new `pub_…` key. Requires workspace_admin. The public key is not authentication — rotating it does not invalidate JWTs or API keys.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/workspaces/{workspaceId}":{"get":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspacesByWorkspaceId","tags":["workspace"],"summary":"Get one of my workspaces","description":"Same item as GET /workspaces for one workspace the caller is a member of (404 otherwise). Dashboard session or a **workspace admin API key** (other API keys: 403 `ERR_PARTNER_SCOPE`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"patch":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"patchApiWorkspacesByWorkspaceId","tags":["workspace"],"summary":"Rename one of my workspaces","description":"Sets `displayName` (1–512 characters). The caller (for a workspace admin key: the admin it is bound to) must be an **admin** of the target workspace (403 otherwise, 404 when not a member). Idempotent; written to the audit log (`workspace.rename`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"}}}}}}},"delete":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"409":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"deleteApiWorkspacesByWorkspaceId","tags":["workspace"],"summary":"Delete (soft) a workspace","description":"Workspaces are never hard-deleted: their charts, events, orders and audit history are kept for accounting and reconciliation. DELETE is a **soft delete** — exactly POST /workspaces/{workspaceId}/actions/deactivate (reversible with …/actions/activate). Dashboard session or a **workspace admin API key** (other API keys: 403 `ERR_PARTNER_SCOPE`); the caller (for a key: the admin it is bound to) must be an **admin** of the target workspace (403 otherwise, 404 when not a member). Idempotent. A deactivated workspace keeps all its data; its API keys, viewer/embed JWTs and public CDN reads are refused with 403 `workspace_deactivated` (effective at once on every API instance through a Redis invalidation message, 5 seconds at most if it is lost), while dashboard members can still sign in so an admin can reactivate it. An admin key cannot deactivate its own workspace (409 `own_workspace`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/workspaces/{workspaceId}/actions/set-default":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"displayName":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]},"planId":{"type":"string"},"subscriptionStatus":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"isCurrent":{"description":"True for the workspace of the calling session (or of the calling workspace admin key).","type":"boolean"},"isDefault":{"description":"The default workspace of the user (for a workspace admin key: of the admin it is bound to): dashboard sign-in opens it. See POST /workspaces/{workspaceId}/actions/set-default.","type":"boolean"},"isTest":{"description":"Test (sandbox) workspace: not billed or metered, `sk_test_` keys, TEST badge for buyers.","type":"boolean"},"parentWorkspaceId":{"description":"Production workspace a test workspace was created from.","anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"description":"`sandbox` exactly when `isTest` (see `/sandboxes`).","anyOf":[{"const":"production","type":"string"},{"const":"sandbox","type":"string"}]}},"required":["workspaceId","displayName","role","planId","subscriptionStatus","status","deactivatedAt","isCurrent","isDefault","isTest","parentWorkspaceId","kind"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"409":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiWorkspacesByWorkspaceIdActionsSet-default","tags":["workspace"],"summary":"Set my default workspace","description":"Makes this workspace the default of the calling user (for a **workspace admin API key**: of the admin it is bound to): dashboard sign-in opens it, and `GET /workspaces` marks it `isDefault`. Any role may set a workspace it is a member of (404 otherwise); a deactivated workspace cannot be the default (409 `workspace_deactivated`). Idempotent; written to the audit log of that workspace (`workspace.set_default`). Other API keys: 403 `ERR_PARTNER_SCOPE`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/workspaces/{workspaceId}/actions/deactivate":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"409":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiWorkspacesByWorkspaceIdActionsDeactivate","tags":["workspace"],"summary":"Deactivate a workspace","description":"Dashboard session or a **workspace admin API key** (other API keys: 403 `ERR_PARTNER_SCOPE`); the caller (for a key: the admin it is bound to) must be an **admin** of the target workspace (403 otherwise, 404 when not a member). Idempotent. A deactivated workspace keeps all its data; its API keys, viewer/embed JWTs and public CDN reads are refused with 403 `workspace_deactivated` (effective at once on every API instance through a Redis invalidation message, 5 seconds at most if it is lost), while dashboard members can still sign in so an admin can reactivate it. An admin key cannot deactivate its own workspace (409 `own_workspace`). You cannot deactivate your last active workspace (409 `last_active_workspace`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/workspaces/{workspaceId}/actions/activate":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"workspaceId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}},"text/plain":{"schema":{"type":"object","properties":{"workspaceId":{"type":"string"},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"deactivatedAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]}},"required":["workspaceId","status","deactivatedAt"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiWorkspacesByWorkspaceIdActionsActivate","tags":["workspace"],"summary":"Reactivate a workspace","description":"Dashboard session or a **workspace admin API key** (other API keys: 403 `ERR_PARTNER_SCOPE`); the caller (for a key: the admin it is bound to) must be an **admin** of the target workspace (403 otherwise, 404 when not a member). Idempotent. A deactivated workspace keeps all its data; its API keys, viewer/embed JWTs and public CDN reads are refused with 403 `workspace_deactivated` (effective at once on every API instance through a Redis invalidation message, 5 seconds at most if it is lost), while dashboard members can still sign in so an admin can reactivate it. An admin key cannot deactivate its own workspace (409 `own_workspace`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/workspace/chart-validation":{"get":{"operationId":"getApiWorkspaceChart-validation","tags":["workspace"],"summary":"Get chart validation severities","description":"Level of every chart validation rule for this workspace (`off`, `warning` or `error`) plus the product defaults. The designer, `POST /charts/{chartId}/map/publish`, `POST /charts/{chartId}/actions/validate` and event creation all apply these levels. Rules: `no_objects` (no seats or GA areas), `empty_label` (unlabeled seats / GA areas), `duplicate_label` (same label in a section), `uncategorized` (objects without a category once categories exist), `overlap`, `table_too_small` (< 2 seats), `ga_invalid_capacity` (GA capacity missing or outside 1–100000), `empty_floor` (multi-floor maps), `empty_section`, `outside_section` (seat outside its section outline), `multiple_object_types` (one category used by seats, table seats and GA areas), `missing_focal_point`, `zone_missing_focal_point`, `orphan_gap` (one-seat gap in a row), `ga_table_capacity` (GA tables in an area hold more places than its capacity), `images_size` (the chart's images download more than 2,048 KB in total).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"patch":{"parameters":[],"operationId":"patchApiWorkspaceChart-validation","tags":["workspace"],"summary":"Update chart validation severities","description":"Workspace admins only. Merges `severities` into the stored levels (only differences from the defaults are kept); `reset: true` restores the defaults first. Takes effect immediately in the designer and the API (other API replicas within milliseconds through a Redis invalidation message, 5 seconds at most if it is lost). Charts are not re-validated: stored per-rule counts are re-read under the new levels. Every change is written to the audit log (`workspace.chart_validation.update`). Rules: `no_objects` (no seats or GA areas), `empty_label` (unlabeled seats / GA areas), `duplicate_label` (same label in a section), `uncategorized` (objects without a category once categories exist), `overlap`, `table_too_small` (< 2 seats), `ga_invalid_capacity` (GA capacity missing or outside 1–100000), `empty_floor` (multi-floor maps), `empty_section`, `outside_section` (seat outside its section outline), `multiple_object_types` (one category used by seats, table seats and GA areas), `missing_focal_point`, `zone_missing_focal_point`, `orphan_gap` (one-seat gap in a row), `ga_table_capacity` (GA tables in an area hold more places than its capacity), `images_size` (the chart's images download more than 2,048 KB in total).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"severities":{"additionalProperties":false,"description":"Level per rule id (`off` = not checked, `warning` = reported, `error` = blocks publish and event creation).","type":"object","properties":{"no_objects":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"duplicate_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"uncategorized":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"overlap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"table_too_small":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_invalid_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_table_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_floor":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"outside_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"multiple_object_types":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"zone_missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"orphan_gap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"images_size":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]}}},"reset":{"description":"Restore every rule to its default before applying `severities`.","type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"severities":{"additionalProperties":false,"description":"Level per rule id (`off` = not checked, `warning` = reported, `error` = blocks publish and event creation).","type":"object","properties":{"no_objects":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"duplicate_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"uncategorized":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"overlap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"table_too_small":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_invalid_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_table_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_floor":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"outside_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"multiple_object_types":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"zone_missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"orphan_gap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"images_size":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]}}},"reset":{"description":"Restore every rule to its default before applying `severities`.","type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"severities":{"additionalProperties":false,"description":"Level per rule id (`off` = not checked, `warning` = reported, `error` = blocks publish and event creation).","type":"object","properties":{"no_objects":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"duplicate_label":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"uncategorized":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"overlap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"table_too_small":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_invalid_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"ga_table_capacity":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_floor":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"empty_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"outside_section":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"multiple_object_types":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"zone_missing_focal_point":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"orphan_gap":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]},"images_size":{"anyOf":[{"const":"off","type":"string"},{"const":"warning","type":"string"},{"const":"error","type":"string"}]}}},"reset":{"description":"Restore every rule to its default before applying `severities`.","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/workspace/defaults":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"renderer":{"description":"Renderer defaults this workspace sets; an absent option uses the built-in default.","type":"object","properties":{"multiSelectEnabled":{"description":"Buyers may select more than one object (built-in: true).","type":"boolean"},"showFullScreenButton":{"description":"The fullscreen button of the seat map (built-in: true).","type":"boolean"},"companionSeatValidationEnabled":{"description":"A selected companion seat needs a selected accessible seat next to it (built-in: true, warn-only).","type":"boolean"},"holdOnSelectForGAs":{"description":"Confirming a quantity in a general admission area holds the places at once (built-in: true).","type":"boolean"}}},"rendererBuiltIn":{"description":"What the seat map does when neither the embed nor the workspace sets an option.","type":"object","required":["multiSelectEnabled","showFullScreenButton","companionSeatValidationEnabled","holdOnSelectForGAs"],"properties":{"multiSelectEnabled":{"type":"boolean"},"showFullScreenButton":{"type":"boolean"},"companionSeatValidationEnabled":{"type":"boolean"},"holdOnSelectForGAs":{"type":"boolean"}}},"draftChartDrawingsEnabled":{"description":"Designer changes go to a draft first (default true). false: every designer save is published directly.","type":"boolean"}},"required":["renderer","rendererBuiltIn","draftChartDrawingsEnabled"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"renderer":{"description":"Renderer defaults this workspace sets; an absent option uses the built-in default.","type":"object","properties":{"multiSelectEnabled":{"description":"Buyers may select more than one object (built-in: true).","type":"boolean"},"showFullScreenButton":{"description":"The fullscreen button of the seat map (built-in: true).","type":"boolean"},"companionSeatValidationEnabled":{"description":"A selected companion seat needs a selected accessible seat next to it (built-in: true, warn-only).","type":"boolean"},"holdOnSelectForGAs":{"description":"Confirming a quantity in a general admission area holds the places at once (built-in: true).","type":"boolean"}}},"rendererBuiltIn":{"description":"What the seat map does when neither the embed nor the workspace sets an option.","type":"object","required":["multiSelectEnabled","showFullScreenButton","companionSeatValidationEnabled","holdOnSelectForGAs"],"properties":{"multiSelectEnabled":{"type":"boolean"},"showFullScreenButton":{"type":"boolean"},"companionSeatValidationEnabled":{"type":"boolean"},"holdOnSelectForGAs":{"type":"boolean"}}},"draftChartDrawingsEnabled":{"description":"Designer changes go to a draft first (default true). false: every designer save is published directly.","type":"boolean"}},"required":["renderer","rendererBuiltIn","draftChartDrawingsEnabled"]}},"text/plain":{"schema":{"type":"object","properties":{"renderer":{"description":"Renderer defaults this workspace sets; an absent option uses the built-in default.","type":"object","properties":{"multiSelectEnabled":{"description":"Buyers may select more than one object (built-in: true).","type":"boolean"},"showFullScreenButton":{"description":"The fullscreen button of the seat map (built-in: true).","type":"boolean"},"companionSeatValidationEnabled":{"description":"A selected companion seat needs a selected accessible seat next to it (built-in: true, warn-only).","type":"boolean"},"holdOnSelectForGAs":{"description":"Confirming a quantity in a general admission area holds the places at once (built-in: true).","type":"boolean"}}},"rendererBuiltIn":{"description":"What the seat map does when neither the embed nor the workspace sets an option.","type":"object","required":["multiSelectEnabled","showFullScreenButton","companionSeatValidationEnabled","holdOnSelectForGAs"],"properties":{"multiSelectEnabled":{"type":"boolean"},"showFullScreenButton":{"type":"boolean"},"companionSeatValidationEnabled":{"type":"boolean"},"holdOnSelectForGAs":{"type":"boolean"}}},"draftChartDrawingsEnabled":{"description":"Designer changes go to a draft first (default true). false: every designer save is published directly.","type":"boolean"}},"required":["renderer","rendererBuiltIn","draftChartDrawingsEnabled"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspaceDefaults","tags":["workspace"],"summary":"Get the workspace defaults (renderer settings, draft chart drawings)","description":"seats.io account `defaultRendererSettings`, `holdOnSelectForGAs` and draft chart drawings. `renderer` lists the defaults this workspace sets for its buyer seat maps (an absent option uses `rendererBuiltIn`): `multiSelectEnabled`, `showFullScreenButton`, `companionSeatValidationEnabled` (the companion-seat selection validator) and `holdOnSelectForGAs`. Resolution per option: explicit embed / host config > workspace default > built-in default. The defaults are served with the cached embed policy (`GET /api/events/{eventId}/embed-policy` → `rendererDefaults`; no per-request query). `draftChartDrawingsEnabled` (default true): with `false` the designer publishes every save directly, also for charts that already have events (auto-publish is no longer refused with 409 `ERR_AUTO_PUBLISH_CHART_HAS_EVENTS`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"patch":{"parameters":[],"operationId":"patchApiWorkspaceDefaults","tags":["workspace"],"summary":"Change the workspace defaults (renderer settings, draft chart drawings)","description":"Workspace admins only (dashboard session; API keys can read, not change: 403). A boolean sets a renderer default, `null` returns it to the built-in default, omitted options are unchanged. Takes effect within seconds on every API instance (tenant config cache invalidation) and within a minute on buyer pages (the viewer caches the embed policy). Audited (`workspace.defaults.update`). seats.io account `defaultRendererSettings`, `holdOnSelectForGAs` and draft chart drawings. `renderer` lists the defaults this workspace sets for its buyer seat maps (an absent option uses `rendererBuiltIn`): `multiSelectEnabled`, `showFullScreenButton`, `companionSeatValidationEnabled` (the companion-seat selection validator) and `holdOnSelectForGAs`. Resolution per option: explicit embed / host config > workspace default > built-in default. The defaults are served with the cached embed policy (`GET /api/events/{eventId}/embed-policy` → `rendererDefaults`; no per-request query). `draftChartDrawingsEnabled` (default true): with `false` the designer publishes every save directly, also for charts that already have events (auto-publish is no longer refused with 409 `ERR_AUTO_PUBLISH_CHART_HAS_EVENTS`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"renderer":{"additionalProperties":false,"type":"object","properties":{"multiSelectEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"showFullScreenButton":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"companionSeatValidationEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"holdOnSelectForGAs":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]}}},"draftChartDrawingsEnabled":{"description":"`false`: publish designer changes directly (no draft step).","type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"renderer":{"additionalProperties":false,"type":"object","properties":{"multiSelectEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"showFullScreenButton":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"companionSeatValidationEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"holdOnSelectForGAs":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]}}},"draftChartDrawingsEnabled":{"description":"`false`: publish designer changes directly (no draft step).","type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"renderer":{"additionalProperties":false,"type":"object","properties":{"multiSelectEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"showFullScreenButton":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"companionSeatValidationEnabled":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]},"holdOnSelectForGAs":{"description":"`null` = use the built-in default (true).","anyOf":[{"type":"boolean"},{"type":"null"}]}}},"draftChartDrawingsEnabled":{"description":"`false`: publish designer changes directly (no draft step).","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/workspace/buyer-extra-data":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Publish the listed keys to buyers (default false: nothing is published).","type":"boolean"},"keys":{"description":"extraData keys buyers may read.","type":"array","items":{"type":"string"}}},"required":["enabled","keys"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"enabled":{"description":"Publish the listed keys to buyers (default false: nothing is published).","type":"boolean"},"keys":{"description":"extraData keys buyers may read.","type":"array","items":{"type":"string"}}},"required":["enabled","keys"]}},"text/plain":{"schema":{"type":"object","properties":{"enabled":{"description":"Publish the listed keys to buyers (default false: nothing is published).","type":"boolean"},"keys":{"description":"extraData keys buyers may read.","type":"array","items":{"type":"string"}}},"required":["enabled","keys"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspaceBuyer-extra-data","tags":["workspace"],"summary":"Get the buyer extraData publication (opt-in, allow-listed keys)","description":"Object `extraData` is private by default (server, Event Manager, reports). With `enabled` and a non-empty `keys` allow-list (at most 16 keys of letters, digits, `_`, `-`, `.` and `:`), only those keys, with scalar values (strings up to 256 characters, numbers, booleans, null), are published for this workspace's events by `GET /api/public/{publicKey}/events/{eventId}/extra-data` and read by the renderer (rules `match.extraData`, templates `{extraData.key}`, host objects). Never part of the compact state. `resalePriceCents` is never published. The key list is kept while disabled.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"put":{"parameters":[],"operationId":"putApiWorkspaceBuyer-extra-data","tags":["workspace"],"summary":"Publish selected extraData keys to buyers (or stop)","description":"Workspace admins only (dashboard session). Replaces the setting; takes effect within seconds on every API instance (tenant config cache invalidation) and on CDN copies of the public read (max-age 5 s). Audited (`workspace.buyer_extra_data.update`). Object `extraData` is private by default (server, Event Manager, reports). With `enabled` and a non-empty `keys` allow-list (at most 16 keys of letters, digits, `_`, `-`, `.` and `:`), only those keys, with scalar values (strings up to 256 characters, numbers, booleans, null), are published for this workspace's events by `GET /api/public/{publicKey}/events/{eventId}/extra-data` and read by the renderer (rules `match.extraData`, templates `{extraData.key}`, host objects). Never part of the compact state. `resalePriceCents` is never published. The key list is kept while disabled.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["enabled","keys"],"properties":{"enabled":{"description":"Publish the listed keys to buyers.","type":"boolean"},"keys":{"maxItems":16,"description":"extraData keys buyers may read (replaces the list).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_.:-]+$","type":"string"}}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["enabled","keys"],"properties":{"enabled":{"description":"Publish the listed keys to buyers.","type":"boolean"},"keys":{"maxItems":16,"description":"extraData keys buyers may read (replaces the list).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_.:-]+$","type":"string"}}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["enabled","keys"],"properties":{"enabled":{"description":"Publish the listed keys to buyers.","type":"boolean"},"keys":{"maxItems":16,"description":"extraData keys buyers may read (replaces the list).","type":"array","items":{"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_.:-]+$","type":"string"}}}}}}},"responses":{"200":{}}}},"/api/workspace/integration":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"},"pricingSourceSet":{"type":"boolean"}},"required":["pricingSource","ownCatalog","pricingSourceSet"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"},"pricingSourceSet":{"type":"boolean"}},"required":["pricingSource","ownCatalog","pricingSourceSet"]}},"text/plain":{"schema":{"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"},"pricingSourceSet":{"type":"boolean"}},"required":["pricingSource","ownCatalog","pricingSourceSet"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspaceIntegration","tags":["workspace"],"summary":"Get the workspace integration mode (pricing source)","description":"`pricingSource`: where prices come from for events without their own override — `partner` (the ticketing platform passes prices to the renderer and declares amounts at confirm; no quote required), `synced_catalog` (the platform pushes a price table with PUT /events/{eventId}/pricing; confirmations are validated against it) or `seatschart_catalog` (prices from the designer catalog; a quote is required before every confirm). New workspaces start with `partner`; workspaces created before pricing modes keep `seatschart_catalog`. `ownCatalog`: show the direct-selling tools (event prices, box office) in the dashboard although prices come from the platform.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"patch":{"parameters":[],"operationId":"patchApiWorkspaceIntegration","tags":["workspace"],"summary":"Change the workspace integration mode","description":"Workspace admins only (dashboard session). Takes effect at once on every API instance (Redis invalidation message; 5 seconds at most if it is lost). Audited (`workspace.integration.update`). `pricingSource`: where prices come from for events without their own override — `partner` (the ticketing platform passes prices to the renderer and declares amounts at confirm; no quote required), `synced_catalog` (the platform pushes a price table with PUT /events/{eventId}/pricing; confirmations are validated against it) or `seatschart_catalog` (prices from the designer catalog; a quote is required before every confirm). New workspaces start with `partner`; workspaces created before pricing modes keep `seatschart_catalog`. `ownCatalog`: show the direct-selling tools (event prices, box office) in the dashboard although prices come from the platform.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"pricingSource":{"anyOf":[{"const":"partner","type":"string"},{"const":"synced_catalog","type":"string"},{"const":"seatschart_catalog","type":"string"}]},"ownCatalog":{"type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/workspace/settings":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"effective":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"defaults":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"sources":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"maxSeatsPerHoldToken":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"ticketBuyerValidation":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"gaPlacesToChannels":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"newEventsNotForSale":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]}}},"overrides":{"type":"object","properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"bounds":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken"],"properties":{"holdPeriodMinutes":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"maxSeatsPerHoldToken":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}},"requireTwoFactor":{"type":"boolean"},"isTest":{"type":"boolean"},"parentWorkspaceId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["effective","defaults","sources","overrides","bounds","requireTwoFactor","isTest","parentWorkspaceId"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"effective":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"defaults":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"sources":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"maxSeatsPerHoldToken":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"ticketBuyerValidation":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"gaPlacesToChannels":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"newEventsNotForSale":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]}}},"overrides":{"type":"object","properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"bounds":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken"],"properties":{"holdPeriodMinutes":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"maxSeatsPerHoldToken":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}},"requireTwoFactor":{"type":"boolean"},"isTest":{"type":"boolean"},"parentWorkspaceId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["effective","defaults","sources","overrides","bounds","requireTwoFactor","isTest","parentWorkspaceId"]}},"text/plain":{"schema":{"type":"object","properties":{"effective":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"defaults":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"sources":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken","ticketBuyerValidation","gaPlacesToChannels","newEventsNotForSale"],"properties":{"holdPeriodMinutes":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"maxSeatsPerHoldToken":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"ticketBuyerValidation":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"gaPlacesToChannels":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]},"newEventsNotForSale":{"anyOf":[{"const":"default","type":"string"},{"const":"override","type":"string"}]}}},"overrides":{"type":"object","properties":{"holdPeriodMinutes":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"maxSeatsPerHoldToken":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"ticketBuyerValidation":{"type":"boolean"},"gaPlacesToChannels":{"type":"boolean"},"newEventsNotForSale":{"type":"boolean"}}},"bounds":{"type":"object","required":["holdPeriodMinutes","maxSeatsPerHoldToken"],"properties":{"holdPeriodMinutes":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"maxSeatsPerHoldToken":{"type":"object","required":["min","max"],"properties":{"min":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"max":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}},"requireTwoFactor":{"type":"boolean"},"isTest":{"type":"boolean"},"parentWorkspaceId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["effective","defaults","sources","overrides","bounds","requireTwoFactor","isTest","parentWorkspaceId"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiWorkspaceSettings","tags":["workspace"],"summary":"Get workspace settings (effective value and source)","description":"Effective value of every workspace setting, the default it inherits and whether it is overridden. Settings: `holdPeriodMinutes` (1–120; default `SEAT_HOLD_TTL_SEC`, 10 min) — TTL of new and renewed seat/GA holds; `maxSeatsPerHoldToken` (1–1000; default 64) — seats one hold token may hold across appends; above 64 the workspace opts into large group holds: one atomic reserve request holds up to this many seats of one event and the sales-channel defaults (web 8, box office 20, partner 64) step aside, while the organizer booking limits still apply; `ticketBuyerValidation` (default off) — buyer tokens are minted only for a registered ticket buyer id (`POST /ticket-buyers`) and bind it, buyer holds must carry it; `gaPlacesToChannels` (default on) — inventory channels may receive GA place quotas (`areaPlaces`); `newEventsNotForSale` (default off) — new events start with every seat and GA area not for sale (seats.io workspace for-sale mode; an explicit `forSaleConfig` at creation wins, season members still follow the season source). Each setting either uses the platform default or is overridden for this workspace; `sources` says which.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"patch":{"parameters":[],"operationId":"patchApiWorkspaceSettings","tags":["workspace"],"summary":"Override workspace settings or return them to the default","description":"Workspace admins only (dashboard session). A value overrides the default for this workspace; `null` returns the setting to the default; omitted settings are unchanged. Takes effect immediately on this API instance and within milliseconds on the others through a Redis invalidation message, 5 seconds at most if it is lost (read from the tenant config cache; no per-request query). Every change is audited (`workspace.settings.update`). Settings: `holdPeriodMinutes` (1–120; default `SEAT_HOLD_TTL_SEC`, 10 min) — TTL of new and renewed seat/GA holds; `maxSeatsPerHoldToken` (1–1000; default 64) — seats one hold token may hold across appends; above 64 the workspace opts into large group holds: one atomic reserve request holds up to this many seats of one event and the sales-channel defaults (web 8, box office 20, partner 64) step aside, while the organizer booking limits still apply; `ticketBuyerValidation` (default off) — buyer tokens are minted only for a registered ticket buyer id (`POST /ticket-buyers`) and bind it, buyer holds must carry it; `gaPlacesToChannels` (default on) — inventory channels may receive GA place quotas (`areaPlaces`); `newEventsNotForSale` (default off) — new events start with every seat and GA area not for sale (seats.io workspace for-sale mode; an explicit `forSaleConfig` at creation wins, season members still follow the season source). Each setting either uses the platform default or is overridden for this workspace; `sources` says which.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","properties":{"holdPeriodMinutes":{"description":"Minutes (1–120); `null` = use the default.","anyOf":[{"minimum":1,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"type":"integer"}]},{"type":"null"}]},"maxSeatsPerHoldToken":{"description":"Seats per hold token (1–1000; above 64 = large group holds); `null` = use the default (64).","anyOf":[{"minimum":1,"maximum":1000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000,"type":"integer"}]},{"type":"null"}]},"ticketBuyerValidation":{"description":"`null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"gaPlacesToChannels":{"description":"`null` = use the default (on).","anyOf":[{"type":"boolean"},{"type":"null"}]},"newEventsNotForSale":{"description":"New events start not for sale; `null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"requireTwoFactor":{"description":"Members must sign in with two-factor authentication (enforced at login with a guided enrolment). Dashboard session only; the caller must have 2FA enabled.","type":"boolean"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","properties":{"holdPeriodMinutes":{"description":"Minutes (1–120); `null` = use the default.","anyOf":[{"minimum":1,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"type":"integer"}]},{"type":"null"}]},"maxSeatsPerHoldToken":{"description":"Seats per hold token (1–1000; above 64 = large group holds); `null` = use the default (64).","anyOf":[{"minimum":1,"maximum":1000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000,"type":"integer"}]},{"type":"null"}]},"ticketBuyerValidation":{"description":"`null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"gaPlacesToChannels":{"description":"`null` = use the default (on).","anyOf":[{"type":"boolean"},{"type":"null"}]},"newEventsNotForSale":{"description":"New events start not for sale; `null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"requireTwoFactor":{"description":"Members must sign in with two-factor authentication (enforced at login with a guided enrolment). Dashboard session only; the caller must have 2FA enabled.","type":"boolean"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","properties":{"holdPeriodMinutes":{"description":"Minutes (1–120); `null` = use the default.","anyOf":[{"minimum":1,"maximum":120,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":120,"type":"integer"}]},{"type":"null"}]},"maxSeatsPerHoldToken":{"description":"Seats per hold token (1–1000; above 64 = large group holds); `null` = use the default (64).","anyOf":[{"minimum":1,"maximum":1000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000,"type":"integer"}]},{"type":"null"}]},"ticketBuyerValidation":{"description":"`null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"gaPlacesToChannels":{"description":"`null` = use the default (on).","anyOf":[{"type":"boolean"},{"type":"null"}]},"newEventsNotForSale":{"description":"New events start not for sale; `null` = use the default (off).","anyOf":[{"type":"boolean"},{"type":"null"}]},"requireTwoFactor":{"description":"Members must sign in with two-factor authentication (enforced at login with a guided enrolment). Dashboard session only; the caller must have 2FA enabled.","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/monitoring/requests":{"get":{"parameters":[{"description":"Window size (default `1h`): 10 minutes, an hour or 24 hours (minute data, kept 24 hours), 7 days (hourly) or 30 days (daily), both kept 31 days.","schema":{"anyOf":[{"const":"10m","type":"string"},{"const":"1h","type":"string"},{"const":"24h","type":"string"},{"const":"7d","type":"string"},{"const":"30d","type":"string"}]},"in":"query","name":"range","required":false},{"description":"History cursor: the window ends just before this instant (aligned down to the bucket source: minute, hour or UTC day). Omit for the live window ending now. Use `previousBefore` / `nextBefore` of a response to step back and forth.","schema":{"type":"string","format":"date-time"},"in":"query","name":"before","required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"range":{"anyOf":[{"const":"10m","type":"string"},{"const":"1h","type":"string"},{"const":"24h","type":"string"},{"const":"7d","type":"string"},{"const":"30d","type":"string"}]},"bucketSeconds":{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","type":"integer"}]},"from":{"format":"date-time","description":"Window start (first bucket).","type":"string"},"to":{"format":"date-time","description":"Window end: now for the live window, else the (exclusive) `before`.","type":"string"},"before":{"description":"Aligned cursor of this window; null for the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"previousBefore":{"description":"`before` of the previous (older) window; null when nothing older is kept.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"nextBefore":{"description":"`before` of the next (newer) window; null when the next window is the live one (omit `before`) or this is the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"retentionStart":{"format":"date-time","description":"Oldest instant kept at this window size (24 hours of minutes, 31 days of hours and days).","type":"string"},"plan":{"description":"Plan whose limits apply (a test workspace uses its production workspace plan).","type":"string"},"isTest":{"type":"boolean"},"windowSeconds":{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","type":"integer"}]},"lanes":{"type":"object","required":["sales","api"],"properties":{"sales":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}},"api":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}}}},"concurrency":{"type":"object","required":["current","peakLastHour","peakLastHourAt","peakLastDay","peakLastDayAt","instances","apiKeys"],"properties":{"current":{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","type":"integer"}]},"peakLastHour":{"description":"Highest concurrent requests in any minute of the last hour.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any minute of the last hour.","type":"integer"}]},"peakLastHourAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"peakLastDay":{"description":"Highest concurrent requests in any hour of the last 24 hours.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any hour of the last 24 hours.","type":"integer"}]},"peakLastDayAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"instances":{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","type":"integer"}]},"apiKeys":{"description":"Per API key, busiest first (keys without traffic in the last 24 hours are omitted).","type":"array","items":{"type":"object","required":["id","name","prefix","current","peakLastHour","peakLastDay"],"properties":{"id":{"description":"API key id.","type":"string"},"name":{"description":"Key name (null when the key was deleted).","anyOf":[{"type":"string"},{"type":"null"}]},"prefix":{"description":"Non-secret key prefix.","anyOf":[{"type":"string"},{"type":"null"}]},"current":{"description":"Requests of this key in flight now on the API instance that answered.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests of this key in flight now on the API instance that answered.","type":"integer"}]},"peakLastHour":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"peakLastDay":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}}},"points":{"type":"array","items":{"type":"object","required":["t","sales","api","salesRejected","apiRejected","concurrent","limited"],"properties":{"t":{"format":"date-time","description":"Bucket start.","type":"string"},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"salesRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"apiRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"concurrent":{"description":"Peak in-flight (concurrent) requests in the bucket.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Peak in-flight (concurrent) requests in the bucket.","type":"integer"}]},"limited":{"description":"True when requests were refused with 429 in the bucket (either lane).","type":"boolean"}}}}},"required":["range","bucketSeconds","from","to","before","previousBefore","nextBefore","retentionStart","plan","isTest","windowSeconds","lanes","concurrency","points"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"range":{"anyOf":[{"const":"10m","type":"string"},{"const":"1h","type":"string"},{"const":"24h","type":"string"},{"const":"7d","type":"string"},{"const":"30d","type":"string"}]},"bucketSeconds":{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","type":"integer"}]},"from":{"format":"date-time","description":"Window start (first bucket).","type":"string"},"to":{"format":"date-time","description":"Window end: now for the live window, else the (exclusive) `before`.","type":"string"},"before":{"description":"Aligned cursor of this window; null for the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"previousBefore":{"description":"`before` of the previous (older) window; null when nothing older is kept.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"nextBefore":{"description":"`before` of the next (newer) window; null when the next window is the live one (omit `before`) or this is the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"retentionStart":{"format":"date-time","description":"Oldest instant kept at this window size (24 hours of minutes, 31 days of hours and days).","type":"string"},"plan":{"description":"Plan whose limits apply (a test workspace uses its production workspace plan).","type":"string"},"isTest":{"type":"boolean"},"windowSeconds":{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","type":"integer"}]},"lanes":{"type":"object","required":["sales","api"],"properties":{"sales":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}},"api":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}}}},"concurrency":{"type":"object","required":["current","peakLastHour","peakLastHourAt","peakLastDay","peakLastDayAt","instances","apiKeys"],"properties":{"current":{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","type":"integer"}]},"peakLastHour":{"description":"Highest concurrent requests in any minute of the last hour.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any minute of the last hour.","type":"integer"}]},"peakLastHourAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"peakLastDay":{"description":"Highest concurrent requests in any hour of the last 24 hours.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any hour of the last 24 hours.","type":"integer"}]},"peakLastDayAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"instances":{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","type":"integer"}]},"apiKeys":{"description":"Per API key, busiest first (keys without traffic in the last 24 hours are omitted).","type":"array","items":{"type":"object","required":["id","name","prefix","current","peakLastHour","peakLastDay"],"properties":{"id":{"description":"API key id.","type":"string"},"name":{"description":"Key name (null when the key was deleted).","anyOf":[{"type":"string"},{"type":"null"}]},"prefix":{"description":"Non-secret key prefix.","anyOf":[{"type":"string"},{"type":"null"}]},"current":{"description":"Requests of this key in flight now on the API instance that answered.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests of this key in flight now on the API instance that answered.","type":"integer"}]},"peakLastHour":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"peakLastDay":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}}},"points":{"type":"array","items":{"type":"object","required":["t","sales","api","salesRejected","apiRejected","concurrent","limited"],"properties":{"t":{"format":"date-time","description":"Bucket start.","type":"string"},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"salesRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"apiRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"concurrent":{"description":"Peak in-flight (concurrent) requests in the bucket.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Peak in-flight (concurrent) requests in the bucket.","type":"integer"}]},"limited":{"description":"True when requests were refused with 429 in the bucket (either lane).","type":"boolean"}}}}},"required":["range","bucketSeconds","from","to","before","previousBefore","nextBefore","retentionStart","plan","isTest","windowSeconds","lanes","concurrency","points"]}},"text/plain":{"schema":{"type":"object","properties":{"range":{"anyOf":[{"const":"10m","type":"string"},{"const":"1h","type":"string"},{"const":"24h","type":"string"},{"const":"7d","type":"string"},{"const":"30d","type":"string"}]},"bucketSeconds":{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"60 for 10m/1h, 900 for 24h, 3600 for 7d, 86400 for 30d.","type":"integer"}]},"from":{"format":"date-time","description":"Window start (first bucket).","type":"string"},"to":{"format":"date-time","description":"Window end: now for the live window, else the (exclusive) `before`.","type":"string"},"before":{"description":"Aligned cursor of this window; null for the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"previousBefore":{"description":"`before` of the previous (older) window; null when nothing older is kept.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"nextBefore":{"description":"`before` of the next (newer) window; null when the next window is the live one (omit `before`) or this is the live window.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"retentionStart":{"format":"date-time","description":"Oldest instant kept at this window size (24 hours of minutes, 31 days of hours and days).","type":"string"},"plan":{"description":"Plan whose limits apply (a test workspace uses its production workspace plan).","type":"string"},"isTest":{"type":"boolean"},"windowSeconds":{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Rate limit window (RATE_LIMIT_TENANT_WINDOW_SEC).","type":"integer"}]},"lanes":{"type":"object","required":["sales","api"],"properties":{"sales":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}},"api":{"type":"object","required":["limitPerMinute","peak","rejected"],"properties":{"limitPerMinute":{"description":"Requests per minute this lane admits on the workspace plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests per minute this lane admits on the workspace plan.","type":"integer"}]},"peak":{"description":"Highest requests in one minute over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest requests in one minute over the range.","type":"integer"}]},"rejected":{"description":"Requests refused with 429 over the range.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests refused with 429 over the range.","type":"integer"}]}}}}},"concurrency":{"type":"object","required":["current","peakLastHour","peakLastHourAt","peakLastDay","peakLastDayAt","instances","apiKeys"],"properties":{"current":{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Workspace requests in flight now on the API instance that answered (this request excluded).","type":"integer"}]},"peakLastHour":{"description":"Highest concurrent requests in any minute of the last hour.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any minute of the last hour.","type":"integer"}]},"peakLastHourAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"peakLastDay":{"description":"Highest concurrent requests in any hour of the last 24 hours.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Highest concurrent requests in any hour of the last 24 hours.","type":"integer"}]},"peakLastDayAt":{"description":"Start of the minute (hour for the day) of the peak.","anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"instances":{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"API instances that reported in-flight peaks in the last 24 hours (peaks are summed across them).","type":"integer"}]},"apiKeys":{"description":"Per API key, busiest first (keys without traffic in the last 24 hours are omitted).","type":"array","items":{"type":"object","required":["id","name","prefix","current","peakLastHour","peakLastDay"],"properties":{"id":{"description":"API key id.","type":"string"},"name":{"description":"Key name (null when the key was deleted).","anyOf":[{"type":"string"},{"type":"null"}]},"prefix":{"description":"Non-secret key prefix.","anyOf":[{"type":"string"},{"type":"null"}]},"current":{"description":"Requests of this key in flight now on the API instance that answered.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Requests of this key in flight now on the API instance that answered.","type":"integer"}]},"peakLastHour":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"peakLastDay":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}}}},"points":{"type":"array","items":{"type":"object","required":["t","sales","api","salesRejected","apiRejected","concurrent","limited"],"properties":{"t":{"format":"date-time","description":"Bucket start.","type":"string"},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"salesRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"apiRejected":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"concurrent":{"description":"Peak in-flight (concurrent) requests in the bucket.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Peak in-flight (concurrent) requests in the bucket.","type":"integer"}]},"limited":{"description":"True when requests were refused with 429 in the bucket (either lane).","type":"boolean"}}}}},"required":["range","bucketSeconds","from","to","before","previousBefore","nextBefore","retentionStart","plan","isTest","windowSeconds","lanes","concurrency","points"]}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiMonitoringRequests","tags":["usage"],"summary":"Request monitoring per rate-limit lane, with in-flight (concurrent) requests","description":"Peak requests per minute and 429 counts per lane for the last 10 minutes, hour, 24 hours, 7 days or 30 days, or for an earlier window of the same size with the `before` cursor (each response carries `previousBefore` / `nextBefore`; every point says whether it had 429s in `limited`). The **sales lane** carries the buyer path (seat map reads, manifest/geometry/state, the live stream, holds, renewals, best-available, releases, quotes, confirmations); the **api lane** carries everything else (reports, events, charts, workspaces, webhooks, keys), so reporting bursts never starve sales. Counters are written by the rate limiter in its existing Redis call (no extra round trip); minute data is kept 24 hours, hourly peaks 31 days. 10m/1h: one bucket per minute; 24h: 15-minute buckets (peak minute); 7d: hourly buckets; 30d: UTC days (rolled up from the hours on the first read and kept as long as the hours). `concurrency` always describes now (not the `before` window). `concurrency` reports requests in flight now and the highest number of concurrent requests in the last hour and day, for the workspace and per API key (live SSE streams are connections and are not counted). Each API instance counts in memory and stores a new per-minute peak in the same limiter call, so with several instances the peaks are summed (an upper bound) and `current` is the instance that answered. Dashboard session (any role) or an API key with the `reports` scope.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/ticket-buyers":{"get":{"parameters":[{"schema":{"minimum":1,"maximum":1000,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":1000,"type":"integer"}]},"in":"query","name":"limit","required":false},{"description":"`nextCursor` of the previous page.","schema":{"type":"string","minLength":1,"maxLength":256},"in":"query","name":"cursor","required":false},{"description":"Only ids starting with this text (case-sensitive).","schema":{"type":"string","minLength":1,"maxLength":256},"in":"query","name":"search","required":false}],"operationId":"getApiTicket-buyers","tags":["embed"],"summary":"List registered ticket buyer ids","description":"Ordered by id; page with `cursor` = the previous `nextCursor` (keep the same `search`). The first page also returns `total` (matching ids). API key (`holds` scope) or a dashboard session that manages integrations.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiTicket-buyers","tags":["embed"],"summary":"Register ticket buyer ids","description":"Adds ids to the workspace allow-list (idempotent). With ticket buyer validation on (`PATCH /workspace/settings`), `POST /viewer-tokens` mints a buyer token only for a registered `ticketBuyerId` and binds it (`tbid` claim): the buyer's holds carry that id, and a different `ticketBuyerId` is refused. Holds never query this list.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}},"responses":{"200":{}}}},"/api/ticket-buyers/actions/remove":{"post":{"parameters":[],"operationId":"postApiTicket-buyersActionsRemove","tags":["embed"],"summary":"Remove ticket buyer ids","description":"Removes ids from the allow-list. Buyer tokens already minted keep their binding until they expire (at most one hour).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["ids"],"properties":{"ids":{"minItems":1,"maxItems":1000,"description":"1–1000 ticket buyer ids.","type":"array","items":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}},"responses":{"200":{}}}},"/api/sandboxes":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}}},"limit":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}},"required":["items","limit"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}}},"limit":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}},"required":["items","limit"]}},"text/plain":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}}},"limit":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}},"required":["items","limit"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiSandboxes","tags":["sandboxes"],"summary":"List the sandboxes of this workspace","description":"Sandboxes of the calling production workspace (oldest first) with this month's usage (shown, never billed) and the rate limits they run at. Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"post":{"parameters":[],"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]},"copied":{"anyOf":[{"type":"object","required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"],"properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}}},{"type":"null"}]}},"required":["sandbox","adminKey","copied"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]},"copied":{"anyOf":[{"type":"object","required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"],"properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}}},{"type":"null"}]}},"required":["sandbox","adminKey","copied"]}},"text/plain":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]},"copied":{"anyOf":[{"type":"object","required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"],"properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}}},{"type":"null"}]}},"required":["sandbox","adminKey","copied"]}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"409":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiSandboxes","tags":["sandboxes"],"summary":"Create a sandbox","description":"Creates an isolated sandbox of this workspace: a workspace of its own (`kind: sandbox`) with its own charts, events, seasons, holds, orders, ticket buyer ids, webhooks and API keys (`sk_test_…`). It is never billed: its usage is shown but not counted against the plan, it has no chart or event caps, webhooks are marked sandbox, billing emails are suppressed and other emails are marked, and buyers see a small Sandbox ribbon. Every member of this workspace becomes a member with the same role, and the team keeps following: members who join later get access with their role, role changes apply and members removed from this workspace lose its sandboxes. At most 10 per workspace (409 `sandbox_limit_reached`). Optionally copies charts (and their events). Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"copyCharts":{"description":"Copy the production charts (every non-archived chart, up to 50). Default false.","type":"boolean"},"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"},"createAdminKey":{"description":"Mint a full-access `sk_test_` key for the sandbox, returned once in `adminKey`. Default true.","type":"boolean"}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"copyCharts":{"description":"Copy the production charts (every non-archived chart, up to 50). Default false.","type":"boolean"},"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"},"createAdminKey":{"description":"Mint a full-access `sk_test_` key for the sandbox, returned once in `adminKey`. Default true.","type":"boolean"}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"minLength":1,"maxLength":512,"type":"string"},"copyCharts":{"description":"Copy the production charts (every non-archived chart, up to 50). Default false.","type":"boolean"},"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"},"createAdminKey":{"description":"Mint a full-access `sk_test_` key for the sandbox, returned once in `adminKey`. Default true.","type":"boolean"}},"additionalProperties":false}}}}}},"/api/sandboxes/{sandboxId}":{"get":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}},"required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}},"required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"]}},"text/plain":{"schema":{"type":"object","properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}},"required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"getApiSandboxesBySandboxId","tags":["sandboxes"],"summary":"Get a sandbox","description":"One sandbox of this workspace (404 for any other id). Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]},"delete":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"deleted":{"const":true,"type":"boolean"},"removed":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}},"required":["sandboxId","deleted","removed"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"deleted":{"const":true,"type":"boolean"},"removed":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}},"required":["sandboxId","deleted","removed"]}},"text/plain":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"deleted":{"const":true,"type":"boolean"},"removed":{"type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}}},"required":["sandboxId","deleted","removed"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"deleteApiSandboxesBySandboxId","tags":["sandboxes"],"summary":"Delete a sandbox","description":"Deletes the sandbox for good in one transaction: its data (as a reset) and its configuration (API keys, webhooks, members, invitations, embed domains). Its keys and sessions stop working at once; members whose default workspace it was default to this workspace. Unlike production workspaces (soft delete), a sandbox is hard-deleted. Irreversible. Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/sandboxes/{sandboxId}/actions/reset":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"deleted":{"description":"Rows deleted per table.","type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]}},"required":["sandbox","deleted","revokedKeys","adminKey"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"deleted":{"description":"Rows deleted per table.","type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]}},"required":["sandbox","deleted","revokedKeys","adminKey"]}},"text/plain":{"schema":{"type":"object","properties":{"sandbox":{"type":"object","required":["sandboxId","displayName","kind","parentWorkspaceId","publicKey","status","createdAt","lastResetAt","usage","rateLimits","role"],"properties":{"sandboxId":{"description":"Workspace id of the sandbox (`ws_…`): switch to it, or call it with its own `sk_test_` key.","type":"string"},"displayName":{"type":"string"},"kind":{"const":"sandbox","type":"string"},"parentWorkspaceId":{"description":"The production workspace the sandbox belongs to.","type":"string"},"publicKey":{"description":"Public renderer key (`pub_…`) of the sandbox; not a secret.","anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"const":"active","type":"string"},{"const":"deactivated","type":"string"}]},"createdAt":{"format":"date-time","type":"string"},"lastResetAt":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}]},"usage":{"type":"object","required":["month","usedObjects","charts","events","activeApiKeys"],"properties":{"month":{"description":"UTC month `YYYY-MM`.","type":"string"},"usedObjects":{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Objects booked in the sandbox this month: shown, never billed or counted against the plan.","type":"integer"}]},"charts":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"events":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"activeApiKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"rateLimits":{"type":"object","required":["plan","api","sales","windowSec"],"properties":{"plan":{"description":"Plan of the production workspace: the sandbox runs at its limits, in buckets of its own.","type":"string"},"api":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"sales":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"windowSec":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"role":{"description":"Role of the calling dashboard user in the sandbox (null for API keys or non-members).","anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"},{"type":"null"}]}}},"deleted":{"description":"Rows deleted per table.","type":"object","patternProperties":{"^(.*)$":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"adminKey":{"anyOf":[{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},{"type":"null"}]}},"required":["sandbox","deleted","revokedKeys","adminKey"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiSandboxesBySandboxIdActionsReset","tags":["sandboxes"],"summary":"Reset a sandbox","description":"Deletes **all data** of the sandbox in one transaction (all or nothing): orders, bookings, holds, events with their inventory, seasons, charts, listings, ticket buyer ids, usage, webhook deliveries and its audit log; live holds are cleared from the cache. Members, settings, the public key, embed domains, API keys (unless `rotateKeys`) and webhook endpoints (unless `keepWebhooks: false`) stay. A concurrent confirmation either commits before the reset (and is deleted) or fails after it. Irreversible. Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rotateKeys":{"description":"Revoke every API key of the sandbox and mint a new full-access key (returned once). Default false: keys are kept.","type":"boolean"},"keepWebhooks":{"description":"Keep the webhook endpoints (their delivery log is always cleared). Default true.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"rotateKeys":{"description":"Revoke every API key of the sandbox and mint a new full-access key (returned once). Default false: keys are kept.","type":"boolean"},"keepWebhooks":{"description":"Keep the webhook endpoints (their delivery log is always cleared). Default true.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"rotateKeys":{"description":"Revoke every API key of the sandbox and mint a new full-access key (returned once). Default false: keys are kept.","type":"boolean"},"keepWebhooks":{"description":"Keep the webhook endpoints (their delivery log is always cleared). Default true.","type":"boolean"}}}}}}}},"/api/sandboxes/{sandboxId}/actions/copy-from-production":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}},"required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}},"required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"]}},"text/plain":{"schema":{"type":"object","properties":{"charts":{"type":"array","items":{"type":"object","required":["sourceChartId","chartId","name","key","published"],"properties":{"sourceChartId":{"type":"string"},"chartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]},"published":{"type":"boolean"}}}},"events":{"type":"array","items":{"type":"object","required":["sourceEventId","eventId","sourceChartId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"sourceChartId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"keyConflicts":{"type":"array","items":{"type":"object","required":["type","id","key"],"properties":{"type":{"anyOf":[{"const":"chart","type":"string"},{"const":"event","type":"string"}]},"id":{"type":"string"},"key":{"type":"string"}}}},"truncatedEvents":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"eventsWithOwnMap":{"description":"Events with their own map (no chart) copied with their event layout: only when every chart is copied (no `chartIds`) with `includeEvents`.","type":"array","items":{"type":"object","required":["sourceEventId","eventId","name","key"],"properties":{"sourceEventId":{"type":"string"},"eventId":{"type":"string"},"name":{"type":"string"},"key":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"seasons":{"description":"Seasons (with partial seasons) whose member events were copied (`includeEvents`), with fresh ids and remapped members.","type":"array","items":{"type":"object","required":["sourceSeasonId","seasonId","name","key","sourceKey","events","partialSeasons"],"properties":{"sourceSeasonId":{"type":"string"},"seasonId":{"type":"string"},"name":{"type":"string"},"key":{"description":"The season key, or null when the sandbox already uses `sourceKey` (copied without a key).","anyOf":[{"type":"string"},{"type":"null"}]},"sourceKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"description":"Member events copied into the season.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Member events copied into the season.","type":"integer"}]},"partialSeasons":{"description":"Partial seasons copied with their copied member events.","anyOf":[{"format":"integer","default":0,"type":"string"},{"description":"Partial seasons copied with their copied member events.","type":"integer"}]}}}}},"required":["charts","events","keyConflicts","truncatedEvents","eventsWithOwnMap","seasons"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiSandboxesBySandboxIdActionsCopy-from-production","tags":["sandboxes"],"summary":"Copy charts (and events) from production into a sandbox","description":"Copies charts of this workspace into the sandbox (the WP18 cross-workspace copy): a published chart becomes a published copy with fresh object ids, a draft-only chart a draft; tags travel with them. With `includeEvents`, the events of each published chart are copied too, with their for-sale configuration and every object available. Chart and event keys are kept when the sandbox does not use them yet (`keyConflicts` lists the others, copied without a key), so the same integration code runs against both workspaces. Sandbox copies never share ids or secrets with production. Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"chartIds":{"minItems":1,"maxItems":50,"uniqueItems":true,"description":"Charts of the production workspace to copy (default: every non-archived chart, up to 50, by name).","type":"array","items":{"format":"uuid","type":"string"}},"includeEvents":{"description":"Also copy the events of each copied published chart (up to 200 per call, newest first) with their for-sale configuration: for-sale flags, channels (new channel keys), GA place quotas, category overrides, event categories, table booking, sales rules, synced prices and the event-level layout. Seasons and partial seasons of the copied events are copied too (fresh ids, remapped members). Nothing sold or held is copied.","type":"boolean"}}}}}}}},"/api/sandboxes/{sandboxId}/admin-key":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"warning":{"type":"string"}},"required":["apiKey","revokedKeys","warning"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"apiKey":{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"warning":{"type":"string"}},"required":["apiKey","revokedKeys","warning"]}},"text/plain":{"schema":{"type":"object","properties":{"apiKey":{"type":"object","required":["id","name","prefix","createdAt","secret"],"properties":{"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"secret":{"description":"Full `sk_test_…` secret, returned exactly once.","type":"string"}}},"revokedKeys":{"anyOf":[{"format":"integer","default":0,"type":"string"},{"type":"integer"}]},"warning":{"type":"string"}},"required":["apiKey","revokedKeys","warning"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiSandboxesBySandboxIdAdmin-key","tags":["sandboxes"],"summary":"Create or rotate the sandbox admin key","description":"Mints a full-access `sk_test_…` key of the sandbox (secret returned once). It works only inside the sandbox: it can never read or change this workspace, another sandbox, or manage workspaces. `revokeExisting: true` rotates (revokes the other keys at once). Callers: a dashboard session of the **production** workspace (any member reads; admins change) or an API key of it (`read` reads; changes need a full-access key). Sandbox keys and sessions get 403 `ERR_SANDBOX_SCOPE`: a sandbox can never act on its production workspace or on another sandbox, and production keys never work inside a sandbox (each workspace has its own keys).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"revokeExisting":{"description":"Rotate: revoke every other key of the sandbox in the same transaction. Default false.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"revokeExisting":{"description":"Rotate: revoke every other key of the sandbox in the same transaction. Default false.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"revokeExisting":{"description":"Rotate: revoke every other key of the sandbox in the same transaction. Default false.","type":"boolean"}}}}}}}},"/api/sandboxes/{sandboxId}/actions/join":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"sandboxId","required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}},"required":["sandboxId","role"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}},"required":["sandboxId","role"]}},"text/plain":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}},"required":["sandboxId","role"]}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"multipart/form-data":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}},"text/plain":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"]}}}}},"operationId":"postApiSandboxesBySandboxIdActionsJoin","tags":["sandboxes"],"summary":"Join a sandbox (dashboard)","description":"Adds the calling admin of the production workspace to one of its sandboxes (as admin; an existing membership is kept) so they can switch to it. Members are also added automatically (listing or reading sandboxes, and accepting an invitation), so this is rarely needed. Dashboard sessions only.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/api/team/members":{"get":{"operationId":"getApiTeamMembers","tags":["team"],"summary":"List workspace members","description":"Each member with role, join date and `twoFactorEnabled` (TOTP two-factor authentication on the account).","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/team/members/{userId}":{"patch":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"userId","required":true}],"operationId":"patchApiTeamMembersByUserId","tags":["team"],"summary":"Change member role","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["role"],"properties":{"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}},"text/plain":{"schema":{"type":"object","required":["role"],"properties":{"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"userId","required":true}],"operationId":"deleteApiTeamMembersByUserId","tags":["team"],"summary":"Remove member from workspace","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/team/invitations":{"get":{"operationId":"getApiTeamInvitations","tags":["team"],"summary":"List pending invitations","security":[{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiTeamInvitations","tags":["team"],"summary":"Invite a teammate by email","description":"Creates a pending invitation and returns a one-time `inviteToken` for the accept link. When `RESEND_API_KEY` and `EMAIL_FROM` are set, an email is also sent; the token remains in the response for manual copy.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}},"multipart/form-data":{"schema":{"type":"object","required":["email","role"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}},"text/plain":{"schema":{"type":"object","required":["email","role"],"properties":{"email":{"minLength":3,"maxLength":320,"type":"string"},"role":{"anyOf":[{"const":"admin","type":"string"},{"const":"map_editor","type":"string"},{"const":"viewer","type":"string"}]}}}}}},"responses":{"200":{}}}},"/api/team/invitations/{invitationId}":{"delete":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"invitationId","required":true}],"operationId":"deleteApiTeamInvitationsByInvitationId","tags":["team"],"summary":"Revoke a pending invitation","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/event-log":{"get":{"parameters":[{"description":"Page size (1..100, default 20).","schema":{"minimum":1,"maximum":100,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"description":"Page size (1..100, default 20).","type":"number"}]},"in":"query","name":"limit","required":false},{"description":"Opaque `nextCursor` value from the previous page.","schema":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[A-Za-z0-9_-]+$"},"in":"query","name":"cursor","required":false},{"description":"Comma-separated event log types (default: all).","schema":{"type":"string","minLength":1,"maxLength":1024},"in":"query","name":"types","required":false},{"description":"Inclusive lower bound (ISO-8601 instant).","schema":{"type":"string","minLength":10,"maxLength":40},"in":"query","name":"from","required":false},{"description":"Exclusive upper bound (ISO-8601 instant).","schema":{"type":"string","minLength":10,"maxLength":40},"in":"query","name":"to","required":false},{"description":"Only entries of this chart or event id.","schema":{"type":"string","minLength":1,"maxLength":128},"in":"query","name":"resourceId","required":false}],"operationId":"getApiEvent-log","tags":["event-log"],"summary":"Event log: admin changes of charts and events (cursor-paginated)","description":"Newest first, from the workspace audit log. Types: `chart.created`, `chart.published`, `chart.draft_discarded`, `chart.version_restored`, `chart.archived`, `chart.unarchived`, `chart.deleted`, `chart.tags_updated`, `chart.categories_updated`, `event.created`, `event.updated`, `event.deleted`, `event.map_published`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated`, `event.table_booking_changed`, `event.sales_rules_updated`, `event.pricing_synced`, `event.moved_to_chart_copy`, `event.season_status_changed`, `event.listings_updated`. Filter with `types` (comma-separated), `from` (inclusive) / `to` (exclusive) ISO-8601 instants and `resourceId` (chart or event id). Pass `nextCursor` back as `cursor` with the same filters until it is null; pages are stable while new entries arrive. `data` carries the change details (never the actor e-mail). The admin types `chart.created`, `event.for_sale_changed`, `event.channels_updated`, `event.categories_updated` and `event.table_booking_changed` are also delivered as webhooks whose items carry `log_id` = the entry `id`. **Retention**: entries are kept for `EVENT_LOG_RETENTION_DAYS` days (default 365, at least 30; seats.io keeps 14) and then deleted with the workspace audit log they come from. API key, or a dashboard member with the integrations permission (admin).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/audit-logs":{"get":{"parameters":[{"schema":{"minimum":1,"maximum":100,"default":50,"anyOf":[{"format":"numeric","default":0,"type":"string"},{"minimum":1,"maximum":100,"default":50,"type":"number"}]},"in":"query","name":"limit","required":false}],"operationId":"getApiAudit-logs","tags":["audit"],"summary":"List recent workspace audit log entries","description":"Append-only config change history for the JWT tenant. Requires `workspace_admin`. Each entry carries where the change came from when known: `ip` (client IP truncated to /24 for IPv4 or /48 for IPv6), `userAgent` and `requestId` (the `x-request-id` of the request that made it); these are never part of the public event log or its webhooks.","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/keys":{"get":{"operationId":"getApiKeys","tags":["api-keys"],"summary":"List active tenant API keys (prefix fingerprint only)","description":"Returns non-revoked keys for the JWT `tenant_id` tenant with their `scopes` and `fullAccess` flag (keys minted before scopes existed have full access). **`x-api-key` partners cannot access this endpoint** (returns `403`).","security":[{"BearerAuth":[]}],"responses":{"200":{}}},"post":{"parameters":[],"operationId":"postApiKeys","tags":["api-keys"],"summary":"Mint a tenant API key (secret returned once)","description":"Generates `sk_live_…` (`sk_test_…` in a test workspace), stores SHA-256 only. Response includes **`secret`** exactly once. `scopes` defaults to full access. `workspaceAdmin: true` (workspace admins only) mints a workspace admin key that can also manage your workspaces via `/api/workspaces`.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["name"],"properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/keys/scopes":{"get":{"operationId":"getApiKeysScopes","tags":["api-keys"],"summary":"List API key scopes and presets","description":"Scopes restrict what an `x-api-key` may call; a missing scope yields `403 {\"error\":\"insufficient_scope\",\"requiredScope\":…}`. Presets: `read_only` = read; `box_office` = read, holds, bookings, reports; `full_access` = every scope. Unlisted mutating routes require a full-access key.","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/keys/{id}":{"patch":{"parameters":[{"description":"API key UUID (lowercase canonical)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"patchApiKeysById","tags":["api-keys"],"summary":"Rename an API key, change its scopes or its workspace admin capability","description":"Scope changes take effect on the next request (the auth cache entry is dropped). The secret is unchanged. `workspaceAdmin: true` binds the key to the calling admin (workspace admins only); `false` removes the capability.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","properties":{"name":{"minLength":1,"maxLength":256,"type":"string"},"scopes":{"minItems":1,"maxItems":16,"description":"Granted scopes: `read`, `holds`, `bookings`, `inventory_admin`, `charts_write`, `events_write`, `reports`, `webhooks`. Granting every scope is full access.","type":"array","items":{"minLength":1,"maxLength":32,"type":"string"}},"workspaceAdmin":{"description":"Workspace admin key: it may also list, activate and deactivate every workspace **you** administer (`/api/workspaces`). Bound to the calling admin and re-checked on each call; requires the `admin` role in this workspace. `false` removes the capability.","type":"boolean"}}}}}},"responses":{"200":{}}},"delete":{"parameters":[{"description":"API key UUID (lowercase canonical)","schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"operationId":"deleteApiKeysById","tags":["api-keys"],"summary":"Soft-revoke an API key","description":"Sets `revoked_at` and drops Redis auth cache entries for this hash.","security":[{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seats/reserve":{"post":{"parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string"},"description":"Partner B2B API key (alternative to Bearer)."},{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"},"description":"Bearer + HS256 JWT for B2C clients."},{"name":"x-tenant-id","in":"header","required":false,"schema":{"type":"string"},"description":"Tenant id for partner integrations."},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"},"description":"Required deduplication key (max 255 chars after trim; UUID v4 recommended). 24h idempotent cache replay in Redis per tenant."}],"operationId":"postApiSeatsReserve","tags":["seats"],"summary":"Place a temporary Redis hold (Lua TTL) before payment","description":"JSON body: `eventId`, `seatIds` (1–64 UUIDs, unique; up to the workspace `maxSeatsPerHoldToken` with large group holds), `userId`, optional `holdToken` capability to append seats to an existing session. `userId` is customer metadata, never ownership proof. Renew/append share a two-hour maximum session lifetime. Optional `ticketBuyerId` (opaque partner buyer id) is stored on a new hold token and inherited by its confirmation. `eventId` also accepts the event key. Headers: **x-api-key** (B2B) *or* **Authorization: Bearer** (HS256 JWT); optional **x-tenant-id**; **`Idempotency-Key` is required** (1–255 chars after trim; use a fresh UUID v4 per checkout attempt). Repeats with the same key within ~24h return the cached HTTP status and JSON without re-running the Lua lock. Applies Redis TTL locks (**default 600 s**, overridden by **`SEAT_HOLD_TTL_SEC`**, or by the workspace **hold period** setting — `PATCH /workspace/settings`) with SSE `seat` → **`locked`**; Postgres remains **`available`** until **`POST /api/seats/confirm`** persists **`sold`** via the reservation worker. A hold token holds at most the workspace `maxSeatsPerHoldToken` seats (409 `seat_limit`). **Large group holds (opt-in):** a workspace `maxSeatsPerHoldToken` above 64 (up to 1000, `PATCH /workspace/settings`) lets one request and one hold token hold that many seats of one event; the request is still one atomic lock — every seat of the group is held under the token, or none is (409 `SEAT_LOCKED` when any seat is taken, nothing locked). The sales-channel defaults (web 8, box office 20, partner 64) then step aside; the organizer's booking limits and the workspace cap still apply. Season and multi-event holds keep the 64-lock limit. **Booking limits:** the organizer's *Max seats per order* (designer → Booking limits, stored as `layoutMeta.bookingRules` of the published chart/event layout) caps the seats one hold token holds, appends included, checked in the same atomic call as the locks (parallel appends cannot exceed it); the strictest of the sales-channel default, this limit, the per-buyer limit and `maxSeatsPerHoldToken` wins. Refused with 409 `booking_limit_exceeded` (`limit`, `current` seats already on the token, `requested`, `kind: \"seats\"`). Holds placed before a lower limit was published keep their seats and stay confirmable; they only cannot grow. With ticket buyer validation on, buyer tokens must carry a bound ticket buyer id (403 `ticket_buyer_required` / `ticket_buyer_mismatch`). Seats that are not for sale are refused with 409 `SEAT_NOT_FOR_SALE`, except a seat with an active **resale** listing in a season member whose for-sale configuration does not propagate (`forSalePropagated: false`, e.g. a season-ticket seat its holder resells for one game); the confirmation and the order commit apply the same rule.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatTicketTypes":{"description":"Optional ticket type per seat (e.g. meia / inteira). Counted atomically against the event quotas when the hold is placed; quote and confirm may still change it within the quota.","type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}},"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatTicketTypes":{"description":"Optional ticket type per seat (e.g. meia / inteira). Counted atomically against the event quotas when the hold is placed; quote and confirm may still change it within the quota.","type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}},"additionalProperties":false}},"text/plain":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"seasonId":{"minLength":36,"maxLength":36,"type":"string"},"eventIds":{"minItems":1,"maxItems":50,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatTicketTypes":{"description":"Optional ticket type per seat (e.g. meia / inteira). Counted atomically against the event quotas when the hold is placed; quote and confirm may still change it within the quota.","type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}},"additionalProperties":false}}}},"responses":{"200":{}}}},"/api/seats/confirm":{"post":{"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Partner B2B API key (required for payment commit)."},{"name":"x-tenant-id","in":"header","required":false,"schema":{"type":"string"},"description":"Ignored for bootstrap env keys (bound to DEFAULT_TENANT_ID). Dashboard `sk_live_…` keys are tenant-scoped by hash."},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"},"description":"Required key (max 255 chars after trim); durable fallback identity when externalOrderId is omitted."}],"operationId":"postApiSeatsConfirm","tags":["seats"],"summary":"Accept a durable confirmation intent before asynchronous seat materialization","description":"Server-to-server only (`x-api-key`). **Pricing source** (workspace integration mode or event sales rules): `partner` events confirm the declared `seatTicketTypes` and `seatPrices` (or `paidAmountCents`) without a quote; `synced_catalog` events validate them against the pushed price table (409 `price_mismatch`, `price_table_missing`, `ticket_type_required`); `seatschart_catalog` events require a durable catalog quote created before external payment. Final ticket types are re-checked against the event quotas and buyer limit (409 `quota_exceeded`, `buyer_limit_exceeded`, `lot_sold_out`). Configure event currency before quoting. The organizer's booking limits are re-checked (defence in depth; holds enforce them atomically): 409 `booking_limit_exceeded` (`limit`, `requested`), except for holds placed before the current limits were published. After verifying payment with your provider, send quoteId, externalPaymentId, paidAmountCents, paidCurrency and the **`holdToken`** capability returned via `POST /api/seats/reserve`. These payment fields are the authenticated partner's attestation, not gateway verification by SeatsChart. Retries of already accepted legacy decisions remain supported without a quote. Consumer JWTs are rejected — confirmation must not run from the browser. Validates Redis holds, then commits the order and requested items in PostgreSQL before 202. Stream delivery is a best-effort trigger; pending SQL items are reconciled by the worker. Provide externalOrderId for stable partner identity. Legacy callers use a durable tenant-scoped Idempotency-Key identity. Season/group holds require scope=hold and the full source seatIds set; all frozen destinations form one atomic order. GET /api/orders/:orderId reports the atomic cart outcome. HTTP keys are durably bound to the authenticated credential and request; changed payload returns 409. Optional `ticketBuyerId` (opaque partner buyer id) is stored on the order, its reservations and object statuses; when omitted it is inherited from the hold token. Quote failures return 409 with `error: quote_required` (no quoteId) or `error: quote_mismatch` (quote or payment attestation does not match).","security":[{"ApiKeyAuth":[]}],"responses":{"409":{"description":"`quote_required`: no `quoteId` on a new confirmation — create a quote with `POST /api/quotes` for the hold, charge exactly its `totalCents`/`currency`, then confirm. `quote_mismatch`: the quote does not belong to this confirmation (event, userId, holdToken, credential, inventory, ticket types) or `externalPaymentId`/`paidAmountCents`/`paidCurrency` differ from it. `idempotency_conflict`: any other conflict (changed payload for the same identity, expired or consumed quote, lost hold).","content":{"application/json":{"schema":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","enum":["quote_required","quote_mismatch","idempotency_conflict"]},"message":{"type":"string"}}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"scope":{"const":"hold","type":"string"},"externalOrderId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatPrices":{"description":"Partner and synced-catalog pricing: amount charged per seat in cents (stored on the order items for reports; validated against the synced price table).","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"scope":{"const":"hold","type":"string"},"externalOrderId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatPrices":{"description":"Partner and synced-catalog pricing: amount charged per seat in cents (stored on the order items for reports; validated against the synced price table).","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}},"text/plain":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"scope":{"const":"hold","type":"string"},"externalOrderId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"seatPrices":{"description":"Partner and synced-catalog pricing: amount charged per seat in cents (stored on the order items for reports; validated against the synced price table).","type":"object","patternProperties":{"^(.*)$":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]}}}}}}}}}},"/api/seats/release":{"post":{"parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string"},"description":"Partner B2B API key (alternative to Bearer)."},{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"},"description":"Bearer + HS256 JWT for B2C clients."},{"name":"x-tenant-id","in":"header","required":false,"schema":{"type":"string"},"description":"Tenant id for partner integrations."},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"},"description":"Required deduplication key (max 255 chars after trim); 24h cache replay in Redis per tenant + route."}],"operationId":"postApiSeatsRelease","tags":["seats"],"summary":"Clear Redis TTL holds before expiry (checkout abandoned / declined)","description":"Deletes KeyDB TTL seat locks when they are authorized by the **`holdToken`** capability, rejects seats already sold in Postgres, publishes **`SSE` `available`** only for locks actually removed. Releasing any seat of a held listing sold as a whole releases the whole listing (its other seats in this hold) in the same atomic call; the response `seatIds` lists every seat released. **`Idempotency-Key`** header is **required**; repeats within ~24h return the cached response (no duplicate side effects). Also: **`x-api-key`** or **`Authorization: Bearer`**.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["eventId","seatIds","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"seatIds":{"minItems":1,"maxItems":1000,"type":"array","items":{"minLength":36,"maxLength":36,"type":"string"}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/orders/{orderId}":{"get":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"orderId","required":true}],"operationId":"getApiOrdersByOrderId","tags":["orders"],"summary":"Read a durable confirmation and its requested items","description":"Partner API key with the `bookings` scope, or a dashboard session of a workspace member (any role); tenant-scoped. Returns the order status, `ticketBuyerId`, seat `items` and general-admission receipts (`gaItems`: quantity, `remaining` places after administrative releases, `cancelledAt`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/orders/{orderId}/cancel":{"post":{"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"orderId","required":true}],"operationId":"postApiOrdersByOrderIdCancel","tags":["orders"],"summary":"Cancel the entire order and preserve its history; does not refund payment","description":"Requires `Idempotency-Key`. Partner API key with the `bookings` scope, or a dashboard session of a workspace admin / map editor; `reason` is stored on the cancelled reservations and receipts. Cancels every seat of the order (seats become free, or retired when not for sale) and, for **administrative bookings**, every general-admission receipt: its remaining places (quantity minus administrative releases) are returned to the area under the GA row lock in the same transaction (`gaAreas: [{ eventId, areaId, returnedPlaces }]`). Receipts stay as history with `cancelledAt`. Retrying a cancelled order returns it unchanged. Orders with non-administrative GA receipts, legacy or failed orders fail closed (409 `inventory_conflict`). Payment refunds are the integrator's responsibility.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"minLength":1,"maxLength":512,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"minLength":1,"maxLength":512,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"minLength":1,"maxLength":512,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/seats/holds/renew":{"post":{"parameters":[],"operationId":"postApiSeatsHoldsRenew","tags":["seats"],"summary":"Renew Redis TTL for all seats in a hold session","description":"Seat sessions require a capability. Renews only existing owned locks, capped at two hours from creation; never recreates expired locks. GA-only tokens retain their separate legacy lifecycle.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/seats/holds/release":{"post":{"parameters":[],"operationId":"postApiSeatsHoldsRelease","tags":["seats"],"summary":"Release all seats held by a hold token","description":"Explicit abandon checkout — clears seat locks and GA holds, emits SSE `seat`/`ga` available. Idempotent when token already expired.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["holdToken"],"properties":{"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}}}},"responses":{"200":{}}}},"/api/quotes":{"post":{"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true}],"operationId":"postApiQuotes","tags":["quotes"],"summary":"Freeze a server-calculated catalog quote before external payment","security":[{"ApiKeyAuth":[]}],"description":"Partner server only. Requires an organizer-configured event currency and live hold. Quotes expire after 10 minutes and do not extend inventory ownership. Amounts, currency, fees and discounts cannot be supplied by the caller. Every new confirm must attest the matching external payment reference, paidAmountCents and paidCurrency together with quoteId; only already accepted legacy decisions may retry without a quote. This API does not verify or charge an external payment gateway.","requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"type":"object","required":["eventId","userId","holdToken"],"properties":{"eventId":{"format":"uuid","type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"format":"uuid","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}},"multipart/form-data":{"schema":{"additionalProperties":false,"type":"object","required":["eventId","userId","holdToken"],"properties":{"eventId":{"format":"uuid","type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"format":"uuid","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}},"text/plain":{"schema":{"additionalProperties":false,"type":"object","required":["eventId","userId","holdToken"],"properties":{"eventId":{"format":"uuid","type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"format":"uuid","type":"string"},"seatTicketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"}}}}}},"responses":{"200":{}}}},"/api/channels":{"get":{"operationId":"getApiChannels","tags":["seats"],"summary":"List sales channels","description":"Returns channel ids with per-order seat limits (web, box_office, partner). Pass `channel` on best-available, reserve, and confirm.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{}}}},"/api/seats/best-available":{"post":{"parameters":[],"operationId":"postApiSeatsBest-available","tags":["seats"],"summary":"Suggest or atomically hold best available seats","description":"Returns seat IDs for the requested quantity, preferring adjacent same-row groups near the map center. Filter the candidate pool by `category` (or ordered `categories`, max 10: windows of the first category are tried before the second, and so on), optional `sectionId` (layout section id or name) and optional `zone` (layout zone id or name; seats resolve explicit zone assignment, then their section zone, then the zone outline) before ranking. `tryToPreventOrphanSeats` (default **true**) skips adjacent windows that would leave a single empty seat at either end of the run, falling back to them only when nothing else fits. `ticketTypes` (`{ ticketTypeId: count }`) sets `quantity` to the sum, restricts the search to categories whose ticket catalog offers every requested type, and returns `ticketTypes` (seatId → ticketTypeId) ready to send as `seatTicketTypes` on confirm; events without ticket types reject it. `accessibleSeats` (N) only returns windows containing at least N seats flagged `accessibility.wheelchair` in the published seat structure (409 when no such window exists). `channel` is salesChannel order limits only. Pass **`x-channel-key`** for inventory partitions — do not send the secret in the body. **`hold: false` (default):** suggest only — does not lock. **`hold: true`:** ranks up to 32 candidate windows across the category preferences, locks the first fit in one Lua call each (all-or-nothing per window), then O(cart) PG gates (available/forSale/channel; seats that are not for sale are never picked, not even resale-listed ones of non-propagating season members: those are bought by holding the seats of the listing); on gate failure releases only the locks it added. Requires **Idempotency-Key**, and `userId` unless `holdToken` is given. `holdToken` appends the seats to that existing hold session (same expiry bound; 409 `hold_token_conflict` when the session is expired, foreign, confirming or full). `extraData` is stored on the held seats (requires hold). Returns `hold_token` for confirm. The organizer's booking limits (designer *Max seats per order*) cap `quantity` and, with `holdToken`, the seats already on the token: best-available never picks more than allowed (409 `booking_limit_exceeded` with `limit`, `current`, `requested`). `sections` (max 20 section ids or names, XOR with `sectionId`) ranks across the union of those sections. **API keys / dashboard sessions only:** `channelKeys` (channel secret keys or ids) restricts the pool to public seats plus those channels, `ignoreChannels: true` allows every channel (both replace `x-channel-key`; buyers get 403). `orderId` (with `hold`) is stored on the held seats as `extraData.orderId` and echoed in the response; pass it as `externalOrderId` on confirm. **`status: \"booked\"`** (API keys with the `bookings` scope, or dashboard sessions allowed to change object statuses; buyers get 403) books the best window directly instead of holding it: the same administrative booking as POST /events/{eventId}/actions/book (one SQL transaction under the seat row locks; a seat held by a buyer is never taken; when a ranked window was taken meanwhile the next one is tried). Requires **Idempotency-Key**: the booking order id is derived from it, so a retry returns the same booking. `ticketTypes` are recorded per seat, `extraData` and `orderId` (as `extraData.orderId`) are stored on the booked seats. Response: `status: \"booked\"`, `booked: true`, `bookingOrderId` (cancel with POST /orders/{orderId}/cancel) and `objects` (status, extraData, orderId, ticketType per seat). `status: \"reservedByToken\"` is the same as `hold: true`. **`keepExtraData: true`** (hold or booked) keeps the seats' stored extraData and merges `extraData` / `orderId` on top instead of replacing it.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]},"category":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"categories":{"minItems":1,"maxItems":10,"type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"ticketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]}}},"sectionId":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"sections":{"minItems":1,"maxItems":20,"description":"Layout section ids or names; the candidate pool is the union of their seats (XOR with sectionId).","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"channelKeys":{"maxItems":64,"description":"API keys only: channel secret keys or channel ids whose seats may be picked (public seats always can).","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"description":"API keys only: pick from every channel.","type":"boolean"},"orderId":{"minLength":1,"maxLength":255,"description":"Requires hold: partner order reference stored on the held seats (extraData.orderId) and echoed back.","type":"string"},"zone":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Layout zone id or name.","type":"string"},{"type":"null"}]},"channel":{"minLength":1,"maxLength":32,"type":"string"},"preferAdjacent":{"type":"boolean"},"tryToPreventOrphanSeats":{"type":"boolean"},"accessibleSeats":{"minimum":0,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":64,"type":"integer"}]},"hold":{"type":"boolean"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"status":{"description":"`booked`: book the best window directly (API keys with the bookings scope / dashboard); `reservedByToken`: same as hold: true.","anyOf":[{"const":"booked","type":"string"},{"const":"reservedByToken","type":"string"}]},"keepExtraData":{"description":"Keep the seats' stored extraData; extraData / orderId are merged on top.","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]},"category":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"categories":{"minItems":1,"maxItems":10,"type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"ticketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]}}},"sectionId":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"sections":{"minItems":1,"maxItems":20,"description":"Layout section ids or names; the candidate pool is the union of their seats (XOR with sectionId).","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"channelKeys":{"maxItems":64,"description":"API keys only: channel secret keys or channel ids whose seats may be picked (public seats always can).","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"description":"API keys only: pick from every channel.","type":"boolean"},"orderId":{"minLength":1,"maxLength":255,"description":"Requires hold: partner order reference stored on the held seats (extraData.orderId) and echoed back.","type":"string"},"zone":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Layout zone id or name.","type":"string"},{"type":"null"}]},"channel":{"minLength":1,"maxLength":32,"type":"string"},"preferAdjacent":{"type":"boolean"},"tryToPreventOrphanSeats":{"type":"boolean"},"accessibleSeats":{"minimum":0,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":64,"type":"integer"}]},"hold":{"type":"boolean"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"status":{"description":"`booked`: book the best window directly (API keys with the bookings scope / dashboard); `reservedByToken`: same as hold: true.","anyOf":[{"const":"booked","type":"string"},{"const":"reservedByToken","type":"string"}]},"keepExtraData":{"description":"Keep the seats' stored extraData; extraData / orderId are merged on top.","type":"boolean"}}}},"text/plain":{"schema":{"type":"object","required":["eventId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]},"category":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"categories":{"minItems":1,"maxItems":10,"type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"ticketTypes":{"type":"object","patternProperties":{"^(.*)$":{"minimum":1,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":64,"type":"integer"}]}}},"sectionId":{"anyOf":[{"minLength":1,"maxLength":128,"type":"string"},{"type":"null"}]},"sections":{"minItems":1,"maxItems":20,"description":"Layout section ids or names; the candidate pool is the union of their seats (XOR with sectionId).","type":"array","items":{"minLength":1,"maxLength":128,"type":"string"}},"channelKeys":{"maxItems":64,"description":"API keys only: channel secret keys or channel ids whose seats may be picked (public seats always can).","type":"array","items":{"minLength":1,"maxLength":64,"type":"string"}},"ignoreChannels":{"description":"API keys only: pick from every channel.","type":"boolean"},"orderId":{"minLength":1,"maxLength":255,"description":"Requires hold: partner order reference stored on the held seats (extraData.orderId) and echoed back.","type":"string"},"zone":{"anyOf":[{"minLength":1,"maxLength":128,"description":"Layout zone id or name.","type":"string"},{"type":"null"}]},"channel":{"minLength":1,"maxLength":32,"type":"string"},"preferAdjacent":{"type":"boolean"},"tryToPreventOrphanSeats":{"type":"boolean"},"accessibleSeats":{"minimum":0,"maximum":64,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":64,"type":"integer"}]},"hold":{"type":"boolean"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"extraData":{"type":"object","patternProperties":{"^(.*)$":{}}},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"status":{"description":"`booked`: book the best window directly (API keys with the bookings scope / dashboard); `reservedByToken`: same as hold: true.","anyOf":[{"const":"booked","type":"string"},{"const":"reservedByToken","type":"string"}]},"keepExtraData":{"description":"Keep the seats' stored extraData; extraData / orderId are merged on top.","type":"boolean"}}}}}},"responses":{"200":{}}}},"/api/ga/reserve":{"post":{"parameters":[{"description":"Required. Reuse with the same request on retry; a different payload conflicts. Seat confirmation binds this key durably in PostgreSQL; hold mutations use bounded Redis receipts.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true}],"operationId":"postApiGaReserve","tags":["seats"],"summary":"Hold GA area capacity (Redis)","description":"Places a temporary hold on unnumbered GA capacity. **Idempotency-Key** is required; repeats within ~24h return the cached hold response. Optional `ticketBuyerId` is stored on the GA hold token and inherited by `POST /ga/confirm`. The organizer's booking limits cap `quantity` (designer *Max GA tickets per order*, else *Max seats per order*; strictest with the sales-channel default): 409 `booking_limit_exceeded` (`limit`, `current`, `requested`, `kind: \"ga\"`). GA tables sold as a whole (`shape.bookAs: \"whole\"`) take every place in one hold: any other `quantity` answers 409 `ga_table_whole` (`capacity`, `requested`). Areas sold as a whole (`areaType` `fixedOccupancy` / `variableOccupancy`) are held for one buyer in one piece: `quantity` is the number of people (fixed: exactly the capacity; variable: `minOccupancy`..capacity), every place of the area is held atomically and a second buyer can never hold part of it. Any other quantity answers 400 `ga_area_whole` (`areaType`, `capacity`, `minOccupancy`, `maxOccupancy`, `requested`); an area already held, sold or partly withheld answers 409 `conflict`. A whole area counts as one object against the booking limits (its capacity, at most 50, caps it); its people count against quotas, lots and per-buyer limits. The response adds `places`, `people` and `areaType`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional ticket type of every place (e.g. meia), counted against the event quotas at hold time.","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional ticket type of every place (e.g. meia), counted against the event quotas at hold time.","type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional ticket type of every place (e.g. meia), counted against the event quotas at hold time.","type":"string"}}}}}},"responses":{"200":{}}}},"/api/ga/confirm":{"post":{"parameters":[{"description":"Required. Reuse with the same request on retry; a different payload conflicts. Seat confirmation binds this key durably in PostgreSQL; hold mutations use bounded Redis receipts.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true}],"operationId":"postApiGaConfirm","tags":["seats"],"summary":"Confirm GA purchase (Postgres sold_count)","description":"Server-to-server only (`x-api-key`): commits GA capacity after payment. SeatsChart-catalog events require a durable quote and matching externalPaymentId/paidAmountCents/paidCurrency; partner-priced events accept the declared paidAmountCents without a quote and synced catalogs validate it (409 `price_mismatch`). The final ticketTypeId is re-checked against the event sales rules (409 `quota_exceeded`, `buyer_limit_exceeded`, `lot_sold_out`) and the quantity against the organizer's booking limits (409 `booking_limit_exceeded`; holds placed before the current limits were published stay confirmable). Previously accepted legacy receipts remain retryable. Consumer JWTs are rejected. Durably records Idempotency-Key and conditionally increments sold_count before reconciling Redis holds. **Idempotency-Key** required. Optional `ticketBuyerId` is stored on the GA receipt (inherited from the hold token when omitted). Quote failures return 409 with `error: quote_required` (no quoteId) or `error: quote_mismatch` (quote or payment attestation does not match). Areas sold as a whole (`fixedOccupancy` / `variableOccupancy`): `quantity` is the number of people held (fixed: the capacity); every place of the area is sold and the receipt records the people. Another quantity answers 400 `ga_area_whole`, a quantity different from the people of the hold token 409 `conflict`. Price: the ticket type's price per person, or once for the area when it is priced per area (`priceBy: \"area\"`). The response adds `places`, `people` and `areaType`.","security":[{"ApiKeyAuth":[]}],"responses":{"409":{"description":"`quote_required`: no `quoteId` on a new confirmation — create a quote with `POST /api/quotes` for the hold, charge exactly its `totalCents`/`currency`, then confirm. `quote_mismatch`: the quote does not belong to this confirmation (event, userId, holdToken, credential, inventory, ticket types) or `externalPaymentId`/`paidAmountCents`/`paidCurrency` differ from it. `conflict`: any other conflict (changed payload for the same identity, expired or consumed quote, lost hold).","content":{"application/json":{"schema":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","enum":["quote_required","quote_mismatch","conflict"]},"message":{"type":"string"}}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["eventId","areaId","quantity","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"quantity":{"minimum":1,"maximum":50,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":1,"maximum":50,"type":"integer"}]},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"},"ticketTypeId":{"minLength":1,"maxLength":64,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"quoteId":{"format":"uuid","type":"string"},"externalPaymentId":{"minLength":1,"maxLength":255,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"paidAmountCents":{"minimum":0,"maximum":2147483647,"anyOf":[{"format":"integer","default":0,"type":"string"},{"minimum":0,"maximum":2147483647,"type":"integer"}]},"paidCurrency":{"pattern":"^[A-Z]{3}$","type":"string"},"channel":{"minLength":1,"maxLength":32,"type":"string"},"ticketBuyerId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000-\\u001F\\u007F\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","description":"Optional opaque ticket buyer id from your system (≤ 256 printable characters). Persisted on holds, orders, reservations, GA receipts, object statuses and status history; never interpreted as an internal user id. Query with GET /events/{eventId}/objects?ticketBuyerId=….","type":"string"}}}}}}}},"/api/ga/release":{"post":{"parameters":[{"description":"Required. Reuse with the same request on retry; a different payload conflicts. Seat confirmation binds this key durably in PostgreSQL; hold mutations use bounded Redis receipts.","schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true}],"operationId":"postApiGaRelease","tags":["seats"],"summary":"Release a GA capacity hold","description":"Clears this user's Redis GA hold for an area. Optional `holdToken` is deleted when it matches tenant/event/user. **Idempotency-Key** required. Emits SSE `ga` and webhook `ga.released`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","areaId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["eventId","areaId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}},"text/plain":{"schema":{"type":"object","required":["eventId","areaId","userId"],"properties":{"eventId":{"minLength":36,"maxLength":36,"type":"string"},"areaId":{"minLength":36,"maxLength":36,"type":"string"},"userId":{"minLength":1,"maxLength":256,"pattern":"^(?:[^\\u0000\\uD800-\\uDFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])+$","type":"string"},"holdToken":{"minLength":36,"maxLength":36,"type":"string"}}}}}},"responses":{"200":{}}}}}}