ROOM Public API v1.0.0

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.

Meta

get/ping

Identify the calling key (whoami)

Returns 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.

scope: nonekeys: pk_, sk_

Responses

200PingResponseSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.

Events

get/events

List published events

Your 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.

scope: events:readkeys: pk_, sk_

Parameters

limitqueryintegerPage size (1–100, default 20).
cursorquerystringOpaque cursor: pass next_cursor from the previous page.
qquerystringFree-text search over title and venue (case-insensitive).
cityquerystringFilter by exact city name (case-insensitive).
scene_idquerystringOnly events in this scene.
starts_afterquerystring (date-time)Only events starting at/after this ISO-8601 timestamp.
starts_beforequerystring (date-time)Only events starting at/before this ISO-8601 timestamp.
scopequeryenum(upcoming | past | all)Time window: upcoming (default), past, or all.
sortqueryenum(start_at | created_at)Sort field (default start_at).
orderqueryenum(asc | desc)Sort direction (default asc).

Responses

200EventListResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/events/by-slug/{slug}

Get event detail by slug

Resolve one of your published events by the slug you use in your own URLs. Same shape as GET /events/{id}.

scope: events:readkeys: pk_, sk_

Parameters

slug *pathstringThe event slug used in your public URLs.

Responses

200EventDetailSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/events/{id}

Get event detail

scope: events:readkeys: pk_, sk_

Parameters

id *pathstringEvent id.

Responses

200EventDetailSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/events/{id}/tiers

List a event’s public ticket tiers

scope: events:readkeys: pk_, sk_

Parameters

id *pathstringEvent id.

Responses

200TierListResponseSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/events/{id}/seating

Get seating layout

Full seating map (zones, seats, legend). Seat status is collapsed to available/unavailable — no holder identity is exposed.

scope: seating:readkeys: pk_, sk_

Parameters

id *pathstringEvent id.
occurrence_idquerystringFor series/recurring events.

Responses

200SeatingResponseSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/events/{id}/availability

Get lightweight availability

Zone/tier availability counts + sales window — cheaper than the full seating map.

scope: availability:readkeys: pk_, sk_

Parameters

id *pathstringEvent id.
occurrence_idquerystringFor series/recurring events.

Responses

200AvailabilityResponseSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.

Seat holds

post/seat-holds

Hold seats (≈15-min TTL)

Reserve 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.

scope: holds:writekeys: pk_, sk_

Parameters

Idempotency-KeyheaderstringOptional 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

200SeatHoldResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorOne or more seats are no longer available (code: seat_conflict).
410ErrorSales are not open for this event (code: sales_closed).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
delete/seat-holds

Release seat holds

scope: holds:writekeys: pk_, sk_

Request body (required)SeatHoldReleaseRequest

Responses

200SeatHoldReleaseResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.

Orders

post/orders

Create an order (guest SBP checkout)

Creates a PENDING ticket order and returns the SBP QR / deep link. Requires an Idempotency-Key header.

scope: orders:writekeys: pk_, sk_

Parameters

Idempotency-Key *headerstringPer-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

200OrderCreateResponseIdempotent replay — the existing order for this Idempotency-Key, in its current state (e.g. already paid/refunded). Same body shape as 201.
201OrderCreateResponseOrder created.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorA 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.
410ErrorSales are closed (code: sales_closed).
422ErrorUnsupported order: free tickets, non-SBP payment method, or multiple events in one order (code: unsupported).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
502ErrorPayment provider error — retry (code: payment_provider_error).
get/orders

List orders (sk_ only)

Your 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.

scope: orders:listkeys: sk_

Parameters

limitqueryintegerPage size (1–100, default 20).
cursorquerystringOpaque cursor: pass next_cursor from the previous page.
statusqueryenum(pending | paid | cancelled | refunded)Filter by order status.
event_idquerystringOnly orders for this event.
created_afterquerystring (date-time)Only orders created at/after this ISO-8601 timestamp.
created_beforequerystring (date-time)Only orders created at/before this ISO-8601 timestamp.

Responses

200OrderListResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
post/orders/preview

Preview pricing (no order created)

scope: orders:writekeys: pk_, sk_

Request body (required)OrderPreviewRequest

Responses

200OrderPreviewResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/orders/{id}

Get order status

Authenticate with an API key (orders:status for status, orders:read also returns buyer PII) OR a poll_token (status only, no PII).

scope: orders:statuskeys: pk_, sk_

Parameters

id *pathstringOrder id.
poll_tokenquerystringBearer-less poll token from order creation (no PII). Alternative to an API key.

Responses

200OrderStatus | OrderStatusWithPIIOrder status. Includes buyer PII only with the orders:read scope.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
post/orders/{id}/check-status

Check order status (POST, uncached)

Identical payload to GET /orders/{id}; POST to bypass intermediary caches while polling.

scope: orders:statuskeys: pk_, sk_

Parameters

id *pathstringOrder id.
poll_tokenquerystringBearer-less poll token from order creation (no PII). Alternative to an API key.

Responses

200OrderStatus | OrderStatusWithPIIOrder status.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
post/orders/{id}/simulate-payment

Simulate payment (test mode only)

Advance a test-mode (mock) order to paid or cancelled and fire the matching webhook. Requires an sk_test key; live orders cannot be simulated.

scope: orders:writekeys: sk_

Parameters

id *pathstringOrder id.

Request bodySimulatePaymentRequest

Responses

200OrderStatus | OrderStatusWithPIIUpdated order status.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorOrder already in a terminal state that conflicts with the requested outcome (code: conflict).
422ErrorNot a test-mode (mock) order, or key is not sk_test (code: unsupported).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/orders/{id}/tickets

Retrieve issued tickets + QR (sk_ only)

For 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.

scope: orders:readkeys: sk_

Parameters

id *pathstringOrder id.

Responses

200OrderTicketsSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorTickets are available only for a paid order — pending (not yet issued) or cancelled/refunded (no longer valid) orders return this (code: conflict).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
post/orders/{id}/refund

Refund a paid ticket order (sk_ only)

Initiate 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.

scope: orders:refundkeys: sk_

Parameters

id *pathstringOrder id.

Request bodyRefundRequestBody

Responses

200RefundRefund initiated, or the completed refund echoed (order already refunded).
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorOrder is not paid, the ticket was scanned/transferred, a transfer is pending, or a refund is already in progress (code: conflict).
422ErrorNot a refundable ticket order: not a ticket, a free ticket, or a non-SBP / transferred-in payment (code: unsupported).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/orders/{id}/refund

Get refund status (sk_ only)

Poll 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).

scope: orders:refundkeys: sk_

Parameters

id *pathstringOrder id.

Responses

200RefundSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.

Webhooks

get/webhook-endpoints

List your webhook endpoints (sk_ only)

Your organizer’s webhook endpoints (to obtain ids for /webhook-deliveries). Secrets are never returned.

scope: webhooks:managekeys: sk_

Responses

200WebhookEndpointListResponseSuccess.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
get/webhook-deliveries

List webhook delivery attempts (sk_ only)

Delivery attempts for your endpoints, newest first. Filter by endpoint_id / status.

scope: webhooks:managekeys: sk_

Parameters

limitqueryintegerPage size (1–100, default 20).
cursorquerystringOpaque cursor: pass next_cursor from the previous page.
endpoint_idquerystringOnly deliveries for this webhook endpoint.
statusqueryenum(pending | delivered | failed | skipped)Filter by delivery status.

Responses

200WebhookDeliveryListResponseSuccess.
400ErrorMalformed or invalid input (code: invalid_request).
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.
post/webhook-deliveries/{id}/redeliver

Re-queue a delivery (sk_ only)

Re-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.

scope: webhooks:managekeys: sk_

Parameters

id *pathstringWebhook delivery id.

Responses

200WebhookDeliveryDelivery re-queued.
401ErrorMissing, invalid, revoked, or expired key (code: unauthorized).
403ErrorInsufficient scope, disallowed origin (pk_), or sk_-only route hit with pk_ (codes: insufficient_scope | origin_not_allowed | forbidden).
404ErrorResource not found or not owned by the key (code: not_found). Cross-tenant access returns 404, never 403.
409ErrorThe endpoint is disabled, or the delivery is already pending (code: conflict).
429ErrorRate limit exceeded (code: rate_limited). See Retry-After / X-RateLimit-* headers.

Webhooks

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.refunded

Error codes

Every error response is the Error envelope{ error: { code, message, param? } }. Branch on the stablecode, never on message.

CodeHTTPWhen
invalid_request400Malformed body, a missing/forbidden field, or an invalid parameter. `param` points at the offender.
unauthorized401Missing, malformed, invalid, revoked, or expired API key — or an invalid poll token.
forbidden403This endpoint requires a secret key (sk_); a publishable key (pk_) was used.
insufficient_scope403The key is missing a scope this operation requires. `param` is "scope".
ip_not_allowed403The request IP is not in this secret key’s IP allow-list. `param` is "ip".
origin_not_allowed403The request Origin is not in this publishable key’s allow-list. `param` is "origin".
not_found404Resource not found, or not owned by this key. Cross-tenant access returns 404, never 403.
conflict409A duplicate request is in flight, or the action conflicts with the resource’s current state.
order_cancelled409The order for this idempotency key was cancelled (e.g. payment could not be created). Start over with a fresh Idempotency-Key.
order_pending409The order is still being prepared (QR not yet stored). Retry shortly — honour Retry-After.
seat_conflict409One or more requested seats are no longer available.
sales_closed410Ticket sales are not open for this event (not started, ended, or event unavailable).
unsupported422Unsupported order: free tickets, a non-SBP payment method, or items spanning multiple events.
rate_limited429A 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_error500Unexpected server error. Safe to retry idempotent reads; for writes, reuse the same Idempotency-Key.
payment_provider_error502The payment provider could not create the charge. Retry with the same Idempotency-Key.

Schemas

Error

Uniform error envelope. `code` is a stable machine-readable string; branch on it (never on `message`).

error *object
Genre
id *string
name *string
slug *string | null
Scene
id *string
name *string
slug *string | null
logo_url *string | null
Tier

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 *integerAdmittable persons per unit (≥1).
seating *booleanTrue if this tier belongs to a seating zone.
NextOccurrence
id *string
starts_at *string (date-time)
EventListItem
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 *stringIANA timezone, e.g. Europe/Moscow.
status *stringLowercase event status, e.g. published.
event_kind *stringsingle | series | recurring.
is_free *boolean
genres *array<Genre>
scene *Scene | null
min_price *integer | nullMinor units (kopecks), or null.
currency *string
qty_total *integer
qty_sold *integer
next_occurrence *NextOccurrence | null
Occurrence
id *string
starts_at *string (date-time)
ends_at *string (date-time)
status *string
title *string | null
description *string | null
cover_url *string | null
Performer
name *string
slug *string | null
avatar_url *string | null
type *stringLowercase performer type, e.g. artist.
starts_at *string | null
duration_min *integer | null
EventDetail
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 | nullMinor units (kopecks), or null.
currency *string
qty_total *integer
qty_sold *integer
tiers *array<Tier>
occurrences *array<Occurrence>
performers *array<Performer>
SeatingLayout
id *string
width *number
height *number
background_url *string | null
decorations *objectOpaque layout decorations (free-form JSON or null).
SeatingPrimaryTier
id *string
name *string
price *integer (int64)Minor units (kopecks). Divide by 100 for RUB.
currency *string
persons_included *integer
StandingTier
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
Seat

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 | nullMinor units (kopecks), or null.
color *string
SeatingZone
id *string
name *string
type *stringseated | standing.
color *string
shape *objectOpaque 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
SeatingLegendItem
tier_id *string
color *string
label *string
price *integer (int64)Minor units (kopecks). Divide by 100 for RUB.
currency *string
SeatingResponse
event_id *string
has_seating *boolean
layout *SeatingLayout | null
zones *array<SeatingZone>
legend *array<SeatingLegendItem>
AvailabilityZone
id *string
name *string
type *string
capacity *number | null
available *number | null
sold_out *boolean
AvailabilityTier
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
AvailabilityResponse
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 | nullMinor units (kopecks), or null.
max_price *integer | nullMinor units (kopecks), or null.
total_available *integer
sold_out *boolean
zones *array<AvailabilityZone> | null
tiers *array<AvailabilityTier>
Buyer
email *string (email)Required. Receipt + ticket delivery.
namestring
phonestringE.164 recommended.
StandingItem
tier_id *string
qty *integer
OrderCreateRequest

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_idstringGeneral-admission ticket tier.
seat_reservation_idsarray<string>reservation_id values returned by POST /seat-holds.
standing_itemsarray<StandingItem>Standing (dancefloor) tiers with quantities.
event_idstring
occurrence_idstringFor series/recurring events.
quantityintegerFor general-admission tier_id orders.
promo_codestring
return_urlstring (uri)HTTPS URL the buyer returns to after paying.
payment_methodenum(sbp)Only SBP is supported via the API.
OrderPreviewRequest

Selection to price. Same selection rules as OrderCreateRequest, but buyer details are not needed.

tier_idstring
seat_reservation_idsarray<string>
standing_itemsarray<StandingItem>
event_idstring
occurrence_idstring
quantityinteger
promo_codestring
OrderCreateResponse
id *string
status *stringpending | 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 *stringBearer-less token to poll this order via GET /orders/{id}?poll_token=… (no PII).
created_at *string (date-time)
OrderPreviewResponse
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
breakdownobject
promoobject
OrderStatus
id *string
type *string
status *stringpending | paid | cancelled | refunded.
is_paid *boolean
livemode *booleanfalse 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 *integerNumber of tickets/items.
event_id *string | null
ticket_tier_id *string | nullGeneral-admission tier id (null for seating/standing orders).
occurrence_tier_id *string | nullPer-occurrence tier id (series/recurring events), else null.
paid_at *string | null
created_at *string (date-time)
OrderStatusWithPII

Order status including buyer PII. Returned only when the key holds the orders:read scope (sk_).

OrderStatus & object

TicketSeat

Seat assignment for a seated ticket.

label *string
zone_name *string
row *string | null
number *integer | null
seat_code *string | null
IssuedTicket

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 *stringROOM-XXXXXX.
status *stringissued | used | transferred | refunded.
seat *objectSeat assignment, or null for general-admission/standing.
OrderTickets

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 *stringAlways paid for this endpoint.
event_id *string | null
quantity *integerNumber of ticket units on the order.
ticket_number *string | nullOrder-level ticket number (the QR encodes this).
qr_payload *string | nullThe single scannable QR payload (JSON string) — render it as a QR for entry. Exactly what the ROOM scanner validates.
tickets *array<IssuedTicket>
PingResponse

The calling key’s own context plus platform capability lists. No buyer PII.

key_id *string
last4 *string | nullLast 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
SeatHoldRequest
event_id *string
occurrence_idstringFor series/recurring events.
seat_ids *array<string>
SeatHoldSeat
reservation_id *stringPass 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.
SeatHoldResponse
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>
SeatHoldReleaseRequest
reservation_ids *array<string>
SeatHoldReleaseResponse
released *integerCount of reservations released (only those owned by the key).
SimulatePaymentRequest
outcomeenum(paid | cancelled)
RefundRequestBody
reasonstringOptional internal note stored on the refund (≤500 chars). Not shown to the buyer.
Refund

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 | nullWhen money actually returned to the buyer (null while pending).
EventListResponse
data *array<EventListItem>
next_cursor *string | nullOpaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=.
has_more *booleanTrue if another page exists (next_cursor is non-null).
TierListResponse
data *array<Tier>
OrderListResponse
data *array<OrderStatus | OrderStatusWithPII>
next_cursor *string | nullOpaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=.
has_more *booleanTrue if another page exists.
WebhookOrder
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)
WebhookEnvelope

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 *stringStable 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
WebhookEndpoint
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)
WebhookEndpointListResponse
data *array<WebhookEndpoint>
WebhookDelivery
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)
WebhookDeliveryListResponse
data *array<WebhookDelivery>
next_cursor *string | nullOpaque cursor for the next page (null on the last page). Pass it back verbatim as ?cursor=.
has_more *booleanTrue if another page exists.