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 Managed 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 |
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.
Choose region when creating the event because its live-inventory Durable
Object is placed on first use 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
}
}holdTtlMs: null means the default 15-minute hold window applies.
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.
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 reports whether existing events refreshed automatically or need a manual update. Only pristine events without private channel allocations can refresh automatically. Events with live state or allocations require the explicit route:
/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 (candidate) |
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 |
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.