An event is an event-owned seating and pricing snapshot plus live inventory. Create it from a published chart, address that inventory through its event key, and update the map deliberately when the source chart changes. Dashboard users can edit an event’s normal prices without changing sibling events on the same chart; existing hold and booking prices remain locked.
For a Platform/API integration, this is an infrastructure Event: it does not imply a SeatLayer catalogue entry, Hosted Event Page, Organizer Website, managed checkout, commerce Order, or ticket. Keep those in your platform unless the organizer deliberately uses the separate Hosted Ticketing product.
Every Event is created from a published reserved-seat Chart. GA areas may exist inside that Chart, but a pure-GA event without a seating chart is not a supported target. Platform/SDK versus Managed commerce ownership is also fixed when the Event is created; reuse the Chart and create a new Event to change products.
Create an event
/v1/eventsSecret key or dashboard sessioncurl -sX POST "https://api.seatlayer.io/v1/events" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY" \
-H "content-type: application/json" \
-H "idempotency-key: event_order_8271" \
-d '{
"chartId": "chart_abc",
"name": "Opening Night",
"slug": "opening-night",
"startsAt": 1767204000000,
"venue": "Main Hall",
"region": "western-europe",
"externalRef": "show_8271",
"currency": "GBP"
}'The chart must be published and belong to an active workspace. A live event requires a positive credit balance; test events do not consume credits.
| Field | Type | Required | Behavior |
|---|---|---|---|
chartId |
string |
Yes | Published source chart |
name |
string |
No | Defaults to chart name |
slug |
string |
No | 3–64 lowercase letters, digits, _, or -; becomes the event key. Slugs are unique per workspace: when another customer already uses one, a numeric suffix is appended (opening-night-2), so always read the key from meta.key |
startsAt |
number |
No | Epoch milliseconds |
venue |
string |
No | Venue label |
mode |
"live" | "test" |
No | Session calls only; a secret key’s mode wins |
externalRef |
string |
No | Host identifier, at most 128 characters |
currency |
string | null |
No | Three-letter event override; otherwise org currency |
description |
string | null |
No | Event description used by share and hosted surfaces |
endsAt |
number | null |
No | End time in epoch milliseconds; cannot precede startsAt |
timezone |
string | null |
No | IANA time zone such as Asia/Kolkata |
region |
string |
No | One of the 11 Event regions; otherwise the workspace default |
locale |
string | null |
No | BCP-47 language tag such as hi-IN |
posterAssetId |
string | null |
No | Previously staged dashboard poster asset |
async |
true |
No | Dashboard sessions only. Answers as soon as the event row is durable and copies the chart pin and inventory behind the response; meta.provisioning is true until they land (typically ~2 s). Ignored for secret-key callers, whose events are always provisioned in the request |
A successful response is 201 {"meta": EventMeta}. Store meta.key beside
your platform’s own event ID; it is the inventory key used by the buyer SDK and
server booking calls. A secret-key-created event starts with Platform checkout
authority and live inventory immediately; it does not require event-page
details or SeatLayer ticket releases. Event creation supports
Idempotency-Key; retry the same logical create with the same key and body.
While meta.provisioning is true (dashboard async creates only), every
route that reads the event’s chart pin or inventory (buyer bootstrap, chart,
availability, holds, and the organizer chart, pricing and report reads)
answers 503 event_provisioning with Retry-After: 2 rather than a 404.
Poll GET /v1/events/{key} until the flag is absent; seatTotal is the
chart’s seat count until then. A secret-key create never returns the flag.
Choose region when creating the event: an event’s live inventory is placed
in its region when the event is first used, and an existing event cannot be
moved by changing later requests. Pick the broad region nearest the event venue, not the
developer, API server, or organizer creating it.
For Rotterdam use western-europe; for India use asia-pacific. Omit region
to inherit the chart workspace’s default. See the 11-region reference and
resolution rules. Placement is a best-effort
latency preference, not a legal data-residency guarantee.
Ticket releases are optional. A Platform integration can keep its own on-sale windows and pricing policy while using SeatLayer inventory, or manage event-scoped releases through the API described below.
List and retrieve
/v1/eventsSecret key or dashboard sessionUse workspaceId to select an active workspace and externalRef to find the
event belonging to a host record:
curl -s \
"https://api.seatlayer.io/v1/events?workspaceId=ws_main&externalRef=show_8271" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY"The response includes events[], with cached metadata, mode, sold, and live
counts where available. A secret key receives only events matching its own
live or test mode. A dashboard session receives both modes for the selected
workspace. Environment tags do not filter event lists or event access.
Mode filtering happens before externalRef, paging, and counts are returned.
When no event in the key’s mode matches the other filters, the list returns
200 {"events":[]}; list calls do not return mode_mismatch. Direct access to
a same-organization event in the opposite mode remains an explicit
403 {"error":"mode_mismatch"}. Unknown and other-organization event keys
remain non-disclosing 404 responses.
Paging
The list is paginated. A response carries at most limit events (default 100,
maximum 200) plus a nextCursor when more remain; the absence of nextCursor
means you have reached the end.
curl -s "https://api.seatlayer.io/v1/events?limit=50" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY"
# → { "events": [ … ], "nextCursor": "MTc1NDA2…" }
curl -s "https://api.seatlayer.io/v1/events?limit=50&cursor=MTc1NDA2…" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY"Cursors are opaque: pass back exactly what you were given and do not construct
one. A cursor we cannot read restarts the list from the beginning rather than
erroring, so a stale bookmark degrades instead of breaking. An oversized limit
clamps to the maximum rather than being rejected.
GET /v1/charts pages the same way, with limit and cursor.
/v1/events/:keySecret key or dashboard sessionThe detail response contains:
{
"meta": {
"key": "opening-night",
"name": "Opening Night",
"chartId": "chart_abc",
"status": "active",
"seatTotal": 1200,
"mode": "live",
"workspaceId": "ws_main",
"externalRef": "show_8271",
"currency": "GBP",
"region": "western-europe",
"sold": 412,
"inventoryModelVersion": 2
},
"counts": {
"free": 760,
"held": 8,
"booked": 412,
"blocked": 20
},
"holdTtlMs": null,
"chartUpdate": {
"behind": false,
"canAutoUpdate": false,
"chartVersionAt": "2026-09-28T14:05:00.000Z"
}
}holdTtlMs: null means the default 15-minute hold window applies.
chartUpdate.chartVersionAt is when the chart version this event runs on was saved, or null when that isn’t known.
Update metadata and section state
/v1/events/:keySecret key or dashboard sessionSend only the fields to change:
{
"name": "Opening Night (Rescheduled)",
"startsAt": 1767290400000,
"venue": "Main Hall",
"externalRef": "show_8271",
"currency": "GBP",
"sectionStates": {
"sec-balcony": "hidden",
"sec-stalls": "open"
}
}startsAt, venue, externalRef, and currency can be cleared with null.
description, endsAt, timezone, and locale can also be updated or cleared.
Section and zone states are open, closed, or hidden; unknown ids return
422. An empty patch returns 400 nothing_to_update.
For timed and demand-triggered section release, use inventory and availability.
Event poster
The poster is the image on the event’s share card when a link to it is posted in chat or social apps. Without one, SeatLayer draws a card from the event name and chart.
/v1/events/:key/posterSecret key or dashboard sessionSend the raw image bytes as the request body, not multipart or base64:
curl -sX PUT "https://api.seatlayer.io/v1/events/opening-night/poster" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY" \
-H "content-type: image/jpeg" \
--data-binary @poster.jpgThe response is the updated event, {"meta": {...}}, with a new posterPath.
Each upload gets a new URL, so share previews that cached the old image pick
up the new one.
| Limit | Value | Error |
|---|---|---|
| Size | Up to 5 MB | 413 image_too_large |
| Format | PNG, JPEG, or WebP, read from the file itself; the content-type header is ignored |
415 unsupported_image |
| Dimensions | Up to 8,000 px per side and 40 megapixels | 422 image_dimensions_unsupported |
| Body | Must not be empty | 400 empty_body |
/v1/events/:key/posterSecret key or dashboard sessionRemoves the poster and returns the event to the generated card. An event with
no poster answers 404 no_poster.
Event Configuration binding
An Event Configuration is a versioned production setup for one chart: stage, screens, barriers, camera platforms, and the seats they take off sale. It is authored, reviewed, and published in the dashboard, then bound to an event. Binding to an event is available through this API.
/v1/events/:key/event-configurationSecret key or dashboard session{
"configuration": { "id": "ec_4f1a…", "version": 3 },
"revision": 2,
"changedBy": "…",
"changedAt": 1767200000000,
"audit": [
{
"id": "…",
"from": null,
"to": { "id": "ec_4f1a…", "version": 3 },
"revision": 1,
"actor": "…",
"createdAt": 1767100000000
}
]
}configuration is null when nothing is bound. audit lists every attach,
replace, and detach.
/v1/events/:key/event-configurationSecret key or dashboard admin{
"expectedRevision": 2,
"configuration": { "id": "ec_4f1a…", "version": 4 }
}Send "configuration": null to detach. expectedRevision must match the
current revision, so two operators cannot overwrite each other.
- Only a
publishedversion can be bound (409 version_not_published). - The version must match the event’s chart revision
(
409 incompatible_configurationwithissues). - Seats the configuration takes off sale must be free. If any is held or
booked, nothing changes (
409 configuration_inventory_seat_conflict). - A stale
expectedRevisionanswers409 configuration_binding_conflictwith the current revision.
Newly killed seats send seat.blocked; seats freed by a replaced or detached
configuration send seat.released. While a configuration is bound,
updating the event’s chart answers
409 event_configuration_bound.
Ticket releases and on-sale waves
Ticket releases are ordered, event-scoped price windows. Each release can have
a start/end time, category scope, quota, and buyer action. Only buy releases
price SeatLayer holds and consume quota; apply and invoice link to the
organizer’s HTTPS workflow and require actionUrl.
/v1/events/:key/releasesSecret key or dashboard sessionThe response returns releases in position order. Quota-bearing releases also
include live consumed and remaining values. remaining: null means the
release has no quota.
/v1/events/:key/releasesSecret key or dashboard sessionPUT replaces the entire ordered list; it is not a per-release patch. Send at
most 12 releases. Omit id for a new release and preserve returned IDs when
editing, because booked and held inventory records the release that priced it.
List order becomes the dense position order returned by the server.
{
"releases": [
{
"name": "Early bird",
"price": 18,
"previousPrice": 22,
"quota": 200,
"startsAt": 1786530600000,
"endsAt": 1787135400000,
"action": "buy"
},
{
"name": "Release 2",
"price": 22,
"action": "buy"
}
]
}Prices are integer major currency units, matching chart category prices. A
categoryKey must exist in the event chart and cannot name a tiered category;
SeatLayer never applies two independent price authorities to one selection.
startsAt and endsAt are epoch milliseconds. An unscoped release applies to
every non-tiered category.
Whole-list replacement is last-write-wins and does not support automatic retry or optimistic concurrency. Serialize release editors per event, read the current list before editing, and confirm the returned list before accepting a write as complete.
/v1/events/:key/releases/:id/closeSecret key or dashboard sessionClosing sets the release’s end time to now and returns the full list. It does not delete the release or erase pricing provenance from existing holds and bookings. Closing an already closed release is safe, but mutations have no automatic-retry contract.
Update an event to the latest chart
Publishing a changed chart never updates an event on its own. Every event
keeps the map it was created or last updated with until you update it, and
the publish response names each linked event that is still on an older map
in linkedEvents, with its booked, held and blocked counts and an
updatePath: safe (nothing to review) or reviewed (existing bookings,
holds and blocks are kept). An event whose reason is
configuration_bound or group_locked is updated from its event
configuration or season instead. refreshedEvents is always 0. To move an
event onto the latest published chart, call the explicit route per event:
/v1/events/:key/update-chartSecret key or dashboard sessioncurl -sX POST \
"https://api.seatlayer.io/v1/events/opening-night/update-chart" \
-H "authorization: Bearer $SEATLAYER_SECRET_KEY"If the update would drop private channel assignments, inspect the returned channel summary and repeat deliberately with an audit reason:
{
"acknowledgeDroppedAssignments": true,
"reason": "Venue reconfiguration approved by operations"
}If a booked or held label would disappear, the update fails atomically:
{
"error": "seats_would_vanish",
"code": "seats_would_vanish",
"missingLabels": ["A-12"],
"message": "The update would remove booked or held seats."
}Archived and deleted events are not mutable. Never work around a conflict by recreating the event; reconcile the affected chart labels and retry.
Lifecycle
| Operation | Route | Buyer sales | Organizer reads/actions |
|---|---|---|---|
| Close | POST /:key/close |
Stopped | Available |
| Reopen | POST /:key/reopen |
Resumed | Available |
| Archive | POST /:key/archive |
Stopped | Reports remain available |
| Restore | POST /:key/unarchive |
Stays stopped | Available again |
| Soft delete | DELETE /:key |
Stopped | Removed from normal event list |
Use close for a reversible pause. Archive after the event ends; SeatLayer writes
a durable inventory snapshot and keeps the live store for reports. Delete is a
soft delete rather than immediate physical erasure. Restoring an archive never
resumes buyer sales implicitly: it returns the event to the active organizer
list with sales paused. Call reopen afterwards to run the normal readiness
gate and resume sales deliberately.
Supported application transitions are:
active -> closed -> active
active or closed -> archived
archived -> active (sales remain paused)
active, closed, or archived -> deletedErrors to handle
| Status | Code | Action |
|---|---|---|
402 |
insufficient_credits |
Top up before creating a new live event |
404 |
chart_not_found / not_found |
Check org, workspace, and resource id |
409 |
event_exists |
Your workspace already has an event with this slug: use a new slug or replay the original idempotent request |
409 |
seats_would_vanish |
Reconcile protected labels |
409 |
season_event_locked |
Amend through the Season workflow or wait until the Season no longer owns the Event |
422 |
chart_not_published |
Publish the chart first |
422 |
invalid_event_inventory |
Fix the chart validation issue |
422 |
workspace_not_found_or_disabled |
Select or enable the workspace |
503 |
event_provisioning |
The event’s pin and inventory are still landing behind a dashboard async create; retry after the Retry-After seconds |
Verification
- Event creation has a stable idempotency key.
- Host records store the returned event key and workspace id.
- Test and live resources use matching secret-key modes.
- The event’s currency, name, start time, and external reference are checked.
- Chart updates are tested with booked and held inventory.
- Close, archive, and delete are explicit operator actions.