/v1/events/:eventKey/bookSecret keyBooking turns a temporary hold into a permanent inventory booking. It does
not create your commercial order or fulfil the purchase. This endpoint is
server-only and accepts sk_test_… or sk_live_… credentials whose mode
matches the event. The event must explicitly use Platform checkout
(buyerCheckout: "integrator"); Managed and unresolved events return 404 so a
server key cannot bypass SeatLayer’s managed checkout.
Request
POST /v1/events/ev_9f3a/book HTTP/1.1
Host: api.seatlayer.io
Authorization: Bearer sk_test_••••••••
Content-Type: application/json
{
"holdId": "hold_01J8F2A7MRQ4",
"labels": ["STALLS-A-12", "STALLS-A-13"],
"bookingRef": "order_1842"
}| Field | Type | Required | Description |
|---|---|---|---|
holdId |
string |
Yes for a buyer flow | Opaque hold identifier returned by the buyer SDK. |
labels |
string[] |
Recommended with holdId |
The seats you priced at checkout, copied from the trusted server-side hold inspection. A hold keeps its id when the buyer changes seats, so pinning the labels makes booking fail with 409 hold_changed instead of booking seats you never charged for. Required for a direct booking without a hold. |
bookingRef |
string |
Yes | Your stable order or reservation reference. It makes retries idempotent and links SeatLayer inventory to your own system. |
Do not send buyer identity, payment details, ticket delivery data, or an amount to this endpoint. Those belong to your platform.
Responses
Every requested object is now booked in SeatLayer inventory.
{
"ok": true,
"booked": ["STALLS-A-12", "STALLS-A-13"]
}On an idempotent replay, booked may be empty because that same
bookingRef already committed the inventory:
{
"ok": true,
"booked": []
}Keep the commercial receipt, buyer, tickets, and delivery state in your own
order record. Only ok and booked belong to the Platform booking
response.
Nothing is booked when any requested object is unavailable.
{
"error": "conflict",
"conflicts": [
{
"label": "STALLS-A-13",
"status": "booked"
}
]
}A test key cannot book live inventory, and a live key cannot book a sandbox event.
{
"error": "mode_mismatch"
}Safe retry behavior
bookingRef is your idempotency key.
Repeating the same request after a timeout
succeeds without creating a second inventory booking or spending credits twice.
Always reuse the same value for the same platform purchase.
const trustedHold = await retrieveSeatLayerHold(eventKey, holdId);
const labels = trustedHold.items.map((item) => item.label);
const order = await priceAndAuthorizePayment(trustedHold); // Your commerce code.
const response = await fetch(
`https://api.seatlayer.io/v1/events/${encodeURIComponent(eventKey)}/book`,
{
method: "POST",
headers: {
authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({ holdId, labels, bookingRef: order.id }),
},
);
if (response.status === 409) {
return recoverInventoryConflict();
}
if (!response.ok) {
// Reconcile order.id through exact Booking History before another action.
throw new Error(`SeatLayer booking failed: ${response.status}`);
}If the connection fails before a response arrives, look up
GET /v1/events/:eventKey/bookings/:bookingRef. Only then decide whether your
workflow should repeat the same event, hold, labels, and bookingRef. Never
change the reference merely because the first outcome is unknown.
Read inventory Booking History
Platform events expose the durable inventory ledger through the official server API:
GET /v1/events/ev_9f3a/bookings?q=order_1842&state=booked&limit=50
Authorization: Bearer sk_test_••••••••Pass the opaque nextCursor back as cursor to read the next page. Search is
limited to inventory identifiers such as booking reference, label, object,
category, section, and sales-channel reference.
GET /v1/events/ev_9f3a/bookings/order_1842
Authorization: Bearer sk_test_••••••••The detail response includes immutable configured-price snapshots, current
object cancellation state, and a chronological book / replay /
partial_cancel / cancel audit. Configured value is operational inventory
value—not the amount charged or settled. These routes never return buyer,
payment, commercial Order, ticket, email, refund, or door data.
The synchronous booking response or this exact Booking History record is the
command outcome. A later seat.booked webhook
is a reconciliation signal; deduplicate it and never use it as permission to
charge the buyer again.
For a resumable background sync, use the
/booking-changes reconciliation feed.
Its append-only activity checkpoint surfaces cancellations to older booking
references without paging the full Booking History again.
Fulfilment and refunds stay in your platform
After a successful inventory booking, your platform confirms its commercial order and creates and delivers its own tickets, QR/barcodes, PDFs, and emails. It also owns ticket recovery, scanning/check-in, refunds, and customer support.
If your platform later cancels or refunds the purchase, call
/unbook with the matching
bookingRef to return inventory to sale. SeatLayer changes inventory on this
route; it does not move or refund your money.