Event series and self-service seat booking
An event series groups the events of one season or competition that share a seating layout, such as the home matches of a club or the photo positions of a tournament. Every event of the series is created from one template event and inherits its prices, seat assignments and seat properties, so it is bookable the moment it exists.
Combined with booking sessions, a series lets accredited people pick their own seat per event in the renderer, while your system confirms the pick with a reference it knows, such as an accreditation id, and reads the result back per event or across the whole series.
When to use a series
- A season on one layout — the same venue and seating plan is used again and again. Create one template event, assign prices and seat properties once, and add an event per match, concert or service day.
- Self-service seat picking — people choose their own seat from the map; nobody pays through the map. Your portal owns the identity, the map owns the seats.
- Media and staff positions — photo positions, press seats or working desks that one accredited person holds per event and that can be released when plans change.
A single event that is not repeated does not need a series: the booking-session flow below works on any published event.
Concepts
| Term | Meaning |
|---|---|
| Template | A published event of your organisation whose prices, seat assignments and seat properties every event of the series copies. |
| Series | The container: a name, the template, a venue time zone and a status (ACTIVE, PAUSED, ARCHIVED). |
| Event | One occurrence in the series, created at a given start. One event exists per start; it keeps its start once created. |
| Session | The browser-side cart that holds seats while a person picks them, created and driven by the renderer without any API key. |
| Reference | The identifier your system stores when it confirms a session, for example an accreditation id. One reference holds one confirmed session per event. |
| Properties | Any JSON object your system stores with the confirmed session, such as a name, an agency or a badge number. Never exposed publicly. |
Authorization
Organisation-side calls send your organisation token in the X-API-Key header. Treat the token as an opaque string and send the value from the Editor login response verbatim.
X-API-Key: {organizationToken}
The public session endpoints used by the renderer need no key. The session id is the credential, so keep the organisation token on your server and let the browser talk to the public endpoints only.
1. Create the series
Publish a template event, assign its prices and, if the renderer needs them, the seat properties. Then create the series from it.
POST /api/private/v2.0/series/
{
"name": "Champions League 2026/27",
"templateEventId": "123e4567-e89b-12d3-a456-426614174000",
"timeZone": "Europe/Paris"
}
The response returns the series with its id, its schemaId and the status ACTIVE. The time zone is the venue’s IANA zone; event start times are interpreted in it when public booking closes at the start.
The template must belong to your organisation, must be published and must not be part of a series itself. To swap the template later, send PUT /api/private/v2.0/series/{id} with the new templateEventId; the new template must use the same schema, and events created earlier keep the assignments they were created with.
2. Add the events
POST /api/private/v2.0/series/{id}/events/
{
"start": "2026-09-15T21:00:00",
"end": "2026-09-15T23:00:00",
"name": "Paris v Munich"
}
The event is created as published with the template’s prices, seat assignments and seat properties, and the response returns it with 201. One event exists per start: repeating the request for the same start returns the existing event with 200, so the call is safe to retry. The name is optional and defaults to the series name followed by the start.
An event that belongs to a series keeps its start. A PUT on the event that moves the start is refused with 409 and the error code EVENT_BOUND_TO_SERIES; the name and end can still change. A PAUSED or ARCHIVED series refuses new events with 409 SERIES_NOT_BOOKABLE.
List the events of a series, earliest first:
GET /api/private/v2.0/series/{id}/events/
3. Let people pick a seat in the renderer
Embed the renderer on the page where the accredited person is already signed in to your portal, loaded with the event of the match they pick. The renderer package exports a BookingSessionClient that drives the public session endpoints from the browser:
createopens a session for the event. Send a fresh idempotency key per attempt; the response carries the session id, its expiry and the seat limit.lockholds the seats the person selects. The seats stay held for the session’s time to live and are refreshed while the person keeps choosing.checkoutfreezes the cart. From here the session waits for your system to confirm it.
Hand the session id to your backend together with the person’s identity. Nothing on the public side names the person; the map only knows the session.
For one seat per person per event, set the session seat limit to one on your installation: the Helm value global.bookingSessions.maxSeats (default 10). The renderer reads the limit from the session and refuses a second seat in the cart.
4. Confirm from your system
Your backend confirms the frozen session with the reference it knows and any object it wants to keep with the booking:
POST /api/private/v2.0/session/{id}/confirm
{
"reference": "ACC-2026-0042",
"properties": { "name": "Ada Lovelace", "agency": "Reuters", "badge": "P-12" }
}
The seats become sold, the session becomes CONFIRMED, and the response returns it with the reference and the properties. The properties are any JSON up to 16 KB serialised; they come back on the session, on the seat assignments read by reference and on the series-wide read below, and never through the public endpoints or the renderer.
One reference holds one confirmed session per event. A second confirmation with the same reference on the same event is refused with 409 REFERENCE_TAKEN; the response names the holder in sessionId whenever it is known. The refused session stays frozen, so cancel it to give its seats back straight away, or leave it to expire. Check whether the person already holds a seat before confirming, and filter by state so that released bookings do not count:
GET /api/private/v2.0/session/?eventId={eventId}&reference=ACC-2026-0042&state=CONFIRMED
A released booking keeps its reference on the cancelled session, so the same reference can be confirmed again.
To change the stored object later, replace it on the confirmed session:
PUT /api/private/v2.0/session/{id}/properties
The request body is the new object itself. It replaces the previous value.
5. Read the seats back
Per event, with the session that holds each seat:
GET /api/private/v2.0/event/{eventId}/prices/assignments/?reference=ACC-2026-0042
The reference filter selects seats. General-admission capacity is counted per area rather than per person, so it is read from the session itself.
Every seat in seatsOnEvent carries its state, its holdType (CART while a session holds or bought it), the seat’s own properties, and a booking object with the sessionId and the reference, plus the session’s properties when the reference filter is used. Without the reference parameter the call returns every seat of the event, booked or not, as before.
Across the whole series, earliest event first:
GET /api/private/v2.0/series/{id}/assignments/?reference=ACC-2026-0042
Each entry names the event (eventId, eventName, start) and the seat with the same fields as above. Without a reference the call lists every seat a session holds or bought on any event of the series.
6. Release a seat
When an accreditation is withdrawn or plans change, release the confirmed session from your system:
POST /api/private/v2.0/session/{id}/release
The seats and any general-admission capacity go back to the map, the session becomes CANCELLED, and the reference is free to book again on that event. Give a confirmed session’s inventory back with this call only. The call is idempotent: repeating it on a cancelled session returns the current state. A session that is not confirmed yet is refused with 409 SESSION_NOT_CONFIRMED; cancel a live session with the cancel endpoint instead. There is no public counterpart: people cannot release a seat from the map.
Booking closes at the start
Public booking for an event of a series closes at the event’s start: creating a session, locking seats and checking out are refused with 409 BOOKING_CLOSED once the start has passed. The start is read in the series’ time zone. Events that do not belong to a series are unaffected and stay bookable after their start. Reading a session, unlocking seats and cancelling keep working, and every organisation-side call, including confirm and release, is unaffected, so a late confirmation from your desk still goes through.
Errors
| Code | Status | Meaning |
|---|---|---|
REFERENCE_TAKEN |
409 | Another confirmed session on this event already holds the reference. |
BOOKING_CLOSED |
409 | The event has started; the public flow no longer accepts changes. |
SESSION_NOT_CONFIRMED |
409 | Release or a properties replacement was called on a session that is not confirmed. |
PROPERTIES_TOO_LARGE |
400 | The properties object exceeds 16 KB serialised. |
SERIES_NOT_BOOKABLE |
409 | The series is paused or archived and refuses new events. |
EVENT_BOUND_TO_SERIES |
409 | The request would move the start of an event that belongs to a series. |
INVALID_SERIES_TEMPLATE |
400 | The template is part of a series or uses a different schema than the series. |
See also
- Direct sale endpoint — sell seats in one call without a session.
- Renderer integration — embedding the map and reading the cart.