Programmatic access to a ROOM organizer’s events, ticket tiers, seating, availability, and guest SBP checkout. All money fields are integers in minor units (kopecks; divide by 100 for RUB). Timestamps are ISO-8601 UTC. Authenticate with a Bearer key (pk_ publishable, browser-safe; sk_ secret, server-only). Cross-tenant access returns 404 (never 403). Every response carries a ROOM-Request-Id header (quote it in support) and a ROOM-Version revision stamp (currently 2026-06-01); successful and rate-limited responses also carry X-RateLimit-Limit/Remaining/Reset. The /v1 path is the major version; additive changes bump ROOM-Version and never break clients (ignore unknown fields, branch on documented enums). Webhooks carry a signed envelope — see the WebhookEnvelope schema.
Base URL https://roombackstage.ru/api/public/v1
Auth Authorization: Bearer pk_… | sk_… — pk_ (browser-safe, origin-bound, no PII); sk_ (server-only, full scope). Each operation lists its required scope + allowed key types.
Money is in minor units (kopecks; ÷100 = RUB). Timestamps are ISO-8601 UTC. Cross-tenant access returns 404.
Every response carries ROOM-Request-Id — quote it in support requests. Successful and rate-limited responses also carry X-RateLimit-Limit / -Remaining / -Reset (seconds); on 429 also Retry-After.
/pingReturns the calling key’s id, masked last4, type, environment, organizer, granted scopes, the webhook event catalog, and the API revision. Any valid key; no specific scope required — useful for an SDK/CLI login step.
nonekeys: pk_, sk_Responses
| 200 | PingResponse | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/eventsYour organizer’s published events. Defaults to upcoming, soonest first. Filter by q / city / scene_id / starts_after / starts_before, switch the time window with scope, and order with sort + order.
events:readkeys: pk_, sk_Parameters
| limit | query | integer | Page size (1–100, default 20). |
| cursor | query | string | Opaque cursor: pass next_cursor from the previous page. |
| q | query | string | Free-text search over title and venue (case-insensitive). |
| city | query | string | Filter by exact city name (case-insensitive). |
| scene_id | query | string | Only events in this scene. |
| starts_after | query | string (date-time) | Only events starting at/after this ISO-8601 timestamp. |
| starts_before | query | string (date-time) | Only events starting at/before this ISO-8601 timestamp. |
| scope | query | enum(upcoming | past | all) | Time window: upcoming (default), past, or all. |
| sort | query | enum(start_at | created_at) | Sort field (default start_at). |
| order | query | enum(asc | desc) | Sort direction (default asc). |
Responses
| 200 | EventListResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/events/by-slug/{slug}Resolve one of your published events by the slug you use in your own URLs. Same shape as GET /events/{id}.
events:readkeys: pk_, sk_Parameters
| slug * | path | string | The event slug used in your public URLs. |
Responses
| 200 | EventDetail | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/events/{id}events:readkeys: pk_, sk_Parameters
| id * | path | string | Event id. |
Responses
| 200 | EventDetail | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/events/{id}/tiersevents:readkeys: pk_, sk_Parameters
| id * | path | string | Event id. |
Responses
| 200 | TierListResponse | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/events/{id}/seatingFull seating map (zones, seats, legend). Seat status is collapsed to available/unavailable — no holder identity is exposed.
seating:readkeys: pk_, sk_Parameters
| id * | path | string | Event id. |
| occurrence_id | query | string | For series/recurring events. |
Responses
| 200 | SeatingResponse | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/events/{id}/availabilityZone/tier availability counts + sales window — cheaper than the full seating map.
availability:readkeys: pk_, sk_Parameters
| id * | path | string | Event id. |
| occurrence_id | query | string | For series/recurring events. |
Responses
| 200 | AvailabilityResponse | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/seat-holdsReserve specific seats before checkout. Use the returned reservation_id values in POST /orders. Optionally send an Idempotency-Key to make network retries safe: retrying with the same key returns the same hold instead of a seat_conflict on your own seats. The key is valid for the hold lifetime (≈15 min). Publishable (pk_) seat-holds are throttled per client IP and capped at a fixed number of concurrent active holds (anti-abuse, since pk_ keys are exposed in browser code); for trusted server-side bulk flows use a secret key (sk_), which is not IP-throttled.
holds:writekeys: pk_, sk_Parameters
| Idempotency-Key | header | string | Optional per-organizer idempotency key (UUID recommended). Retrying with the same key returns the same hold while it is alive (≈15-min TTL). |
Request body (required) — SeatHoldRequest
Responses
| 200 | SeatHoldResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | One or more seats are no longer available (code: seat_conflict). |
| 410 | Error | Sales are not open for this event (code: sales_closed). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/seat-holdsholds:writekeys: pk_, sk_Request body (required) — SeatHoldReleaseRequest
Responses
| 200 | SeatHoldReleaseResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/ordersCreates a PENDING ticket order and returns the SBP QR / deep link. Requires an Idempotency-Key header.
orders:writekeys: pk_, sk_Parameters
| Idempotency-Key * | header | string | Per-organizer idempotency key (UUID recommended). The key is bound to its order for the order’s whole lifetime (no TTL): retrying returns the same order in its current state — including paid/refunded (the idempotent result). While the order is still being prepared a retry may return 409 order_pending (honour Retry-After). If the prior order for the key was cancelled, a retry returns 409 order_cancelled — start over with a FRESH key. A key is single-use per order and is never freed for reuse: mint a new key (e.g. crypto.randomUUID()) per checkout attempt. |
Request body (required) — OrderCreateRequest
Responses
| 200 | OrderCreateResponse | Idempotent replay — the existing order for this Idempotency-Key, in its current state (e.g. already paid/refunded). Same body shape as 201. |
| 201 | OrderCreateResponse | Order created. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | A duplicate is in flight, the prior order for this idempotency key is cancelled, or the order is still being prepared (codes: conflict | order_cancelled | order_pending). Honour Retry-After when present. |
| 410 | Error | Sales are closed (code: sales_closed). |
| 422 | Error | Unsupported order: free tickets, non-SBP payment method, or multiple events in one order (code: unsupported). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
| 502 | Error | Payment provider error — retry (code: payment_provider_error). |
/ordersYour organizer’s ticket orders, newest first. Filter by status / event_id / created_after / created_before. Buyer PII is included only if the key also holds orders:read.
orders:listkeys: sk_Parameters
| limit | query | integer | Page size (1–100, default 20). |
| cursor | query | string | Opaque cursor: pass next_cursor from the previous page. |
| status | query | enum(pending | paid | cancelled | refunded) | Filter by order status. |
| event_id | query | string | Only orders for this event. |
| created_after | query | string (date-time) | Only orders created at/after this ISO-8601 timestamp. |
| created_before | query | string (date-time) | Only orders created at/before this ISO-8601 timestamp. |
Responses
| 200 | OrderListResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/previeworders:writekeys: pk_, sk_Request body (required) — OrderPreviewRequest
Responses
| 200 | OrderPreviewResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}Authenticate with an API key (orders:status for status, orders:read also returns buyer PII) OR a poll_token (status only, no PII).
orders:statuskeys: pk_, sk_Parameters
| id * | path | string | Order id. |
| poll_token | query | string | Bearer-less poll token from order creation (no PII). Alternative to an API key. |
Responses
| 200 | OrderStatus | OrderStatusWithPII | Order status. Includes buyer PII only with the orders:read scope. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}/check-statusIdentical payload to GET /orders/{id}; POST to bypass intermediary caches while polling.
orders:statuskeys: pk_, sk_Parameters
| id * | path | string | Order id. |
| poll_token | query | string | Bearer-less poll token from order creation (no PII). Alternative to an API key. |
Responses
| 200 | OrderStatus | OrderStatusWithPII | Order status. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}/simulate-paymentAdvance a test-mode (mock) order to paid or cancelled and fire the matching webhook. Requires an sk_test key; live orders cannot be simulated.
orders:writekeys: sk_Parameters
| id * | path | string | Order id. |
Request body — SimulatePaymentRequest
Responses
| 200 | OrderStatus | OrderStatusWithPII | Updated order status. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | Order already in a terminal state that conflicts with the requested outcome (code: conflict). |
| 422 | Error | Not a test-mode (mock) order, or key is not sk_test (code: unsupported). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}/ticketsFor a PAID ticket order: the single scannable qr_payload (render it as a QR for entry) plus the per-unit ticket breakdown (number, status, seat, holder). Secret key only — the QR is an entry credential.
orders:readkeys: sk_Parameters
| id * | path | string | Order id. |
Responses
| 200 | OrderTickets | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | Tickets are available only for a paid order — pending (not yet issued) or cancelled/refunded (no longer valid) orders return this (code: conflict). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}/refundInitiate a full (100%) refund of a paid TICKET order you own. Processed asynchronously over SBP — the status starts pending and becomes completed once money actually returns to the buyer (the order.refunded webhook fires then). Idempotent: once the order is refunded, a repeat call echoes the completed refund; while a refund is still in progress it returns 409 — poll GET to follow it.
orders:refundkeys: sk_Parameters
| id * | path | string | Order id. |
Request body — RefundRequestBody
Responses
| 200 | Refund | Refund initiated, or the completed refund echoed (order already refunded). |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | Order is not paid, the ticket was scanned/transferred, a transfer is pending, or a refund is already in progress (code: conflict). |
| 422 | Error | Not a refundable ticket order: not a ticket, a free ticket, or a non-SBP / transferred-in payment (code: unsupported). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/orders/{id}/refundPoll the latest refund for an order. Returns 404 if the order has no active or completed refund (none initiated, or the last attempt was rejected).
orders:refundkeys: sk_Parameters
| id * | path | string | Order id. |
Responses
| 200 | Refund | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/webhook-endpointsYour organizer’s webhook endpoints (to obtain ids for /webhook-deliveries). Secrets are never returned.
webhooks:managekeys: sk_Responses
| 200 | WebhookEndpointListResponse | Success. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/webhook-deliveriesDelivery attempts for your endpoints, newest first. Filter by endpoint_id / status.
webhooks:managekeys: sk_Parameters
| limit | query | integer | Page size (1–100, default 20). |
| cursor | query | string | Opaque cursor: pass next_cursor from the previous page. |
| endpoint_id | query | string | Only deliveries for this webhook endpoint. |
| status | query | enum(pending | delivered | failed | skipped) | Filter by delivery status. |
Responses
| 200 | WebhookDeliveryListResponse | Success. |
| 400 | Error | Malformed or invalid input (code: invalid_request). |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
/webhook-deliveries/{id}/redeliverRe-queues a non-pending delivery (failed, skipped, or already delivered) for another send attempt with a fresh attempt schedule. Sent shortly after, behind live events. Returns the updated delivery.
webhooks:managekeys: sk_Parameters
| id * | path | string | Webhook delivery id. |
Responses
| 200 | WebhookDelivery | Delivery re-queued. |
| 401 | Error | Missing, invalid, revoked, or expired key (code: unauthorized). |
| 403 | Error | Insufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden). |
| 404 | Error | Resource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403. |
| 409 | Error | The endpoint is disabled, or the delivery is already pending (code: conflict). |
| 429 | Error | Rate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers. |
ROOM POSTs a signed WebhookEnvelope to endpoints you register in the dashboard. Verify ROOM-Signature (HMAC-SHA256 over `${t}.${rawBody}`, tolerance 300s) and dedupe on the envelope id. api_version: 2026-06-01.
order.paidorder.cancelledorder.refundedEvery error response is the Error envelope{ error: { code, message, param? } }. Branch on the stablecode, never on message.
| Code | HTTP | When |
|---|---|---|
| invalid_request | 400 | Malformed body, a missing/forbidden field, or an invalid parameter. `param` points at the offender. |
| unauthorized | 401 | Missing, malformed, invalid, revoked, or expired API key — or an invalid poll token. |
| forbidden | 403 | This endpoint requires a secret key (sk_); a publishable key (pk_) was used. |
| insufficient_scope | 403 | The key is missing a scope this operation requires. `param` is "scope". |
| ip_not_allowed | 403 | The request IP is not in this secret key’s IP allow-list. `param` is "ip". |
| origin_not_allowed | 403 | The request Origin is not in this publishable key’s allow-list. `param` is "origin". |
| not_found | 404 | Resource not found, or not owned by this key. Cross-tenant access returns 404, never 403. |
| conflict | 409 | A duplicate request is in flight, or the action conflicts with the resource’s current state. |
| order_cancelled | 409 | The order for this idempotency key was cancelled (e.g. payment could not be created). Start over with a fresh Idempotency-Key. |
| order_pending | 409 | The order is still being prepared (QR not yet stored). Retry shortly — honour Retry-After. |
| seat_conflict | 409 | One or more requested seats are no longer available. |
| sales_closed | 410 | Ticket sales are not open for this event (not started, ended, or event unavailable). |
| unsupported | 422 | Unsupported order: free tickets, a non-SBP payment method, or items spanning multiple events. |
| rate_limited | 429 | A rate limit was exceeded — the per-key budget, or (for publishable pk_ seat-holds) a per-IP rate / concurrent-hold cap. Honour Retry-After / X-RateLimit-* headers, then retry. |
| internal_error | 500 | Unexpected server error. Safe to retry idempotent reads; for writes, reuse the same Idempotency-Key. |
| payment_provider_error | 502 | The payment provider could not create the charge. Retry with the same Idempotency-Key. |
Uniform error envelope. `code` is a stable machine-readable string; branch on it (never on `message`).
| error * | object |
| id * | string | |
| name * | string | |
| slug * | string | null |
| id * | string | |
| name * | string | |
| slug * | string | null | |
| logo_url * | string | null |
A public ticket tier.
| id * | string | |
| name * | string | |
| description * | string | null | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string | |
| qty_total * | integer | |
| qty_sold * | integer | |
| qty_available * | integer | |
| sold_out * | boolean | |
| persons_included * | integer | Admittable persons per unit (≥1). |
| seating * | boolean | True if this tier belongs to a seating zone. |
| id * | string | |
| starts_at * | string (date-time) |
| id * | string | |
| title * | string | |
| slug * | string | null | |
| description * | string | null | |
| city * | string | |
| venue * | string | |
| cover_url * | string | null | |
| cover_wide_url * | string | null | |
| cover_poster_url * | string | null | |
| latitude * | number | null | |
| longitude * | number | null | |
| starts_at * | string (date-time) | |
| ends_at * | string (date-time) | |
| timezone * | string | IANA timezone, e.g. Europe/Moscow. |
| status * | string | Lowercase event status, e.g. published. |
| event_kind * | string | single | series | recurring. |
| is_free * | boolean | |
| genres * | array<Genre> | |
| scene * | Scene | null | |
| min_price * | integer | null | Minor units (kopecks), or null. |
| currency * | string | |
| qty_total * | integer | |
| qty_sold * | integer | |
| next_occurrence * | NextOccurrence | null |
| id * | string | |
| starts_at * | string (date-time) | |
| ends_at * | string (date-time) | |
| status * | string | |
| title * | string | null | |
| description * | string | null | |
| cover_url * | string | null |
| name * | string | |
| slug * | string | null | |
| avatar_url * | string | null | |
| type * | string | Lowercase performer type, e.g. artist. |
| starts_at * | string | null | |
| duration_min * | integer | null |
| id * | string | |
| title * | string | |
| slug * | string | null | |
| description * | string | null | |
| city * | string | |
| venue * | string | |
| address * | string | null | |
| cover_url * | string | null | |
| cover_wide_url * | string | null | |
| cover_poster_url * | string | null | |
| latitude * | number | null | |
| longitude * | number | null | |
| starts_at * | string (date-time) | |
| ends_at * | string (date-time) | |
| timezone * | string | |
| status * | string | |
| event_kind * | string | |
| is_free * | boolean | |
| age_restriction * | integer | null | |
| sales_start_at * | string | null | |
| sales_end_at * | string | null | |
| has_seating * | boolean | |
| genres * | array<Genre> | |
| scene * | Scene | null | |
| min_price * | integer | null | Minor units (kopecks), or null. |
| currency * | string | |
| qty_total * | integer | |
| qty_sold * | integer | |
| tiers * | array<Tier> | |
| occurrences * | array<Occurrence> | |
| performers * | array<Performer> |
| id * | string | |
| width * | number | |
| height * | number | |
| background_url * | string | null | |
| decorations * | object | Opaque layout decorations (free-form JSON or null). |
| id * | string | |
| name * | string | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string | |
| persons_included * | integer |
| id * | string | |
| name * | string | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string | |
| persons_included * | integer | |
| color * | string | |
| available * | integer | |
| sold_out * | boolean |
A seat. `status` is collapsed to available|unavailable — holder identity is never exposed.
| id * | string | |
| label * | string | |
| x * | number | |
| y * | number | |
| rotation * | number | |
| status * | string | |
| ticket_tier_id * | string | null | |
| price * | integer | null | Minor units (kopecks), or null. |
| color * | string |
| id * | string | |
| name * | string | |
| type * | string | seated | standing. |
| color * | string | |
| shape * | object | Opaque zone geometry (free-form JSON or null). |
| sort_order * | number | |
| capacity * | number | null | |
| available * | number | null | |
| sold_out * | boolean | |
| primary_tier * | SeatingPrimaryTier | null | |
| standing_tiers * | array<StandingTier> | null | |
| seats * | array<Seat> | null |
| tier_id * | string | |
| color * | string | |
| label * | string | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string |
| event_id * | string | |
| has_seating * | boolean | |
| layout * | SeatingLayout | null | |
| zones * | array<SeatingZone> | |
| legend * | array<SeatingLegendItem> |
| id * | string | |
| name * | string | |
| type * | string | |
| capacity * | number | null | |
| available * | number | null | |
| sold_out * | boolean |
| id * | string | |
| name * | string | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string | |
| qty_total * | integer | |
| qty_sold * | integer | |
| qty_available * | integer | |
| sold_out * | boolean |
| event_id * | string | |
| status * | string | |
| has_seating * | boolean | |
| sales_start_at * | string | null | |
| sales_end_at * | string | null | |
| is_sales_open * | boolean | |
| currency * | string | |
| min_price * | integer | null | Minor units (kopecks), or null. |
| max_price * | integer | null | Minor units (kopecks), or null. |
| total_available * | integer | |
| sold_out * | boolean | |
| zones * | array<AvailabilityZone> | null | |
| tiers * | array<AvailabilityTier> |
| email * | string (email) | Required. Receipt + ticket delivery. |
| name | string | |
| phone | string | E.164 recommended. |
| tier_id * | string | |
| qty * | integer |
Provide exactly ONE selection: tier_id (general admission), seat_reservation_ids (from POST /seat-holds), or standing_items. All items must belong to a single event.
| buyer * | Buyer | |
| tier_id | string | General-admission ticket tier. |
| seat_reservation_ids | array<string> | reservation_id values returned by POST /seat-holds. |
| standing_items | array<StandingItem> | Standing (dancefloor) tiers with quantities. |
| event_id | string | |
| occurrence_id | string | For series/recurring events. |
| quantity | integer | For general-admission tier_id orders. |
| promo_code | string | |
| return_url | string (uri) | HTTPS URL the buyer returns to after paying. |
| payment_method | enum(sbp) | Only SBP is supported via the API. |
Selection to price. Same selection rules as OrderCreateRequest, but buyer details are not needed.
| tier_id | string | |
| seat_reservation_ids | array<string> | |
| standing_items | array<StandingItem> | |
| event_id | string | |
| occurrence_id | string | |
| quantity | integer | |
| promo_code | string |
| id * | string | |
| status * | string | pending | paid | cancelled. |
| type * | string | |
| environment * | enum(test | live) | |
| currency * | string | |
| base_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| service_fee * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| total_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| payment * | object | |
| poll_token * | string | Bearer-less token to poll this order via GET /orders/{id}?poll_token=… (no PII). |
| created_at * | string (date-time) |
| type * | string | |
| item_name * | string | null | |
| event_title * | string | null | |
| quantity * | integer | |
| base_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| service_fee * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| total_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| currency * | string | |
| fee_version * | integer | |
| is_free * | boolean | |
| breakdown | object | |
| promo | object |
| id * | string | |
| type * | string | |
| status * | string | pending | paid | cancelled | refunded. |
| is_paid * | boolean | |
| livemode * | boolean | false for test-mode (mock) orders; ignore those on production fulfilment. |
| currency * | string | |
| base_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| service_fee * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| total_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| quantity * | integer | Number of tickets/items. |
| event_id * | string | null | |
| ticket_tier_id * | string | null | General-admission tier id (null for seating/standing orders). |
| occurrence_tier_id * | string | null | Per-occurrence tier id (series/recurring events), else null. |
| paid_at * | string | null | |
| created_at * | string (date-time) |
Order status including buyer PII. Returned only when the key holds the orders:read scope (sk_).
OrderStatus & object
Seat assignment for a seated ticket.
| label * | string | |
| zone_name * | string | |
| row * | string | null | |
| number * | integer | null | |
| seat_code * | string | null |
One issued ticket (one per unit of quantity). Informational breakdown — the scannable credential is the order-level qr_payload. Holder identity is intentionally not exposed.
| ticket_number * | string | ROOM-XXXXXX. |
| status * | string | issued | used | transferred | refunded. |
| seat * | object | Seat assignment, or null for general-admission/standing. |
Issued tickets for a PAID order. ONE scannable QR per order (qr_payload) — the door admits the whole order in one scan; tickets[] is the per-unit breakdown.
| order_id * | string | |
| status * | string | Always paid for this endpoint. |
| event_id * | string | null | |
| quantity * | integer | Number of ticket units on the order. |
| ticket_number * | string | null | Order-level ticket number (the QR encodes this). |
| qr_payload * | string | null | The single scannable QR payload (JSON string) — render it as a QR for entry. Exactly what the ROOM scanner validates. |
| tickets * | array<IssuedTicket> |
The calling key’s own context plus platform capability lists. No buyer PII.
| key_id * | string | |
| last4 * | string | null | Last 4 chars of the key (mask) for self-identification. |
| key_type * | enum(pk | sk) | |
| environment * | enum(test | live) | |
| livemode * | boolean | |
| organizer_id * | string | |
| scopes * | array<string> | Scopes granted to THIS key. |
| webhook_event_types * | array<string> | Event types you can subscribe a webhook to. |
| api_version * | string |
| event_id * | string | |
| occurrence_id | string | For series/recurring events. |
| seat_ids * | array<string> |
| reservation_id * | string | Pass to POST /orders (seat_reservation_ids) or DELETE /seat-holds. |
| seat_id * | string | |
| label * | string | |
| zone_name * | string | |
| zone_color * | string | |
| ticket_tier_id * | string | |
| price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| hold_id * | string | |
| expires_at * | string (date-time) | Hold TTL (~15 min). Create the order before this. |
| total_base_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| seats * | array<SeatHoldSeat> |
| reservation_ids * | array<string> |
| released * | integer | Count of reservations released (only those owned by the key). |
| outcome | enum(paid | cancelled) |
| reason | string | Optional internal note stored on the refund (≤500 chars). Not shown to the buyer. |
A refund of a paid ticket order. Always a 100% refund in v1. status=completed ⟺ money has actually returned to the buyer (the order.refunded webhook has fired).
| id * | string | |
| order_id * | string | |
| status * | enum(pending | completed) | pending = initiated, money in flight (async SBP settlement or an ops-handled manual reverse); completed = money returned (order.refunded fired). |
| amount * | integer (int64) | Amount refunded to the buyer. Equals the order total for a 100% refund. |
| currency * | string | |
| reason * | string | null | |
| created_at * | string (date-time) | |
| completed_at * | string | null | When money actually returned to the buyer (null while pending). |
| data * | array<EventListItem> | |
| next_cursor * | string | null | Opaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=. |
| has_more * | boolean | True if another page exists (next_cursor is non-null). |
| data * | array<Tier> |
| data * | array<OrderStatus | OrderStatusWithPII> | |
| next_cursor * | string | null | Opaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=. |
| has_more * | boolean | True if another page exists. |
| id * | string | |
| type * | string | |
| status * | string | |
| is_paid * | boolean | |
| livemode * | boolean | |
| currency * | string | |
| base_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| service_fee * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| total_price * | integer (int64) | Minor units (kopecks). Divide by 100 for RUB. |
| quantity * | integer | |
| event_id * | string | null | |
| ticket_tier_id * | string | null | |
| occurrence_tier_id * | string | null | |
| paid_at * | string | null | |
| created_at * | string (date-time) |
Outbound webhook payload. Signed with ROOM-Signature: t=<unix>,v1=<hmac-sha256>. Verify HMAC over `${t}.${rawBody}` and reject if |now-t| > 300s. Dedupe on id. api_version is "2026-06-01".
| id * | string | Stable envelope id (evt_…); same across retries — dedupe on it. |
| type * | enum(order.paid | order.cancelled | order.refunded) | |
| api_version * | string | |
| created_at * | string (date-time) | |
| data * | object |
| id * | string | |
| url * | string | |
| status * | enum(active | disabled) | |
| enabled_events * | array<string> | |
| description * | string | null | |
| consecutive_failures * | integer | |
| last_delivery_at * | string | null | |
| created_at * | string (date-time) |
| data * | array<WebhookEndpoint> |
| id * | string | |
| endpoint_id * | string | |
| event_type * | string | |
| order_id * | string | null | |
| status * | enum(pending | delivered | failed | skipped) | |
| attempts * | integer | |
| max_attempts * | integer | |
| last_status_code * | integer | null | |
| last_error * | string | null | |
| next_attempt_at * | string (date-time) | |
| delivered_at * | string | null | |
| failed_at * | string | null | |
| created_at * | string (date-time) |
| data * | array<WebhookDelivery> | |
| next_cursor * | string | null | Opaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=. |
| has_more * | boolean | True if another page exists. |