Skip to content

Event Series API

Make several dates from one floor plan or from one event you set up, each a normal event sold on its own, and add dates later with the same seats and prices.

Updated View as Markdown

An event series is a run of dates from one floor plan: a play every night for three weeks, a matinee and an evening each Saturday, a cinema’s showtimes. Each date is a normal event with its own key. You sell it, report on it and check it in exactly like any other event, through the Events API.

If the dates must be bought together, use a Performance Group instead. If buyers hold a seat for a whole season and renew it, use Seasons.

Every route on this page takes a matching sk_test_… or sk_live_… secret key. A test key makes test events; a live key makes live events, which use credits like any other live event.

A key can’t make a date public. The paste-in run tag lists public dates that sell through SeatLayer checkout, so make the dates public from the dashboard first.

How the dates are planned

You send a schedule, not a list of timestamps. SeatLayer plans the dates with the same engine the dashboard’s New event sheet uses, so a series made by API and one made in the dashboard have the same dates.

Field Meaning
rule none (one day), daily, weekly, biweekly, or dates (the days in dates)
from The first day, YYYY-MM-DD. Not used by dates
until or count The last day (inclusive), or how many days. With neither, one day
times One to six start times, HH:MM. Each day gets one date per time
skip Moments to leave out, YYYY-MM-DDTHH:MM: “Sunday has no evening show”
dates For rule: dates: the days, YYYY-MM-DD

Times are read in the series’ timezone (an IANA name such as Europe/London). A start time stays the same on the clock across a daylight saving change: a 19:30 show is at 19:30 in October and in November. Without a timezone, the usual zone of this floor plan’s events is used, else UTC.

Every date carries the series name as its name, Hamlet, with nothing added. The dates are told apart by startsAt and linked by seriesKey, so a ticket page can show one title with its times. For that reason a series name cannot hold {n}, {date} or {time}.

A series holds at most 366 dates. One call adds at most 100.

Preview, then create

POST/v1/event-seriesSecret key

Send dryRun: true to see the dates without creating anything:

curl https://api.seatlayer.io/v1/event-series \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Hamlet",
    "chartKey": "c_41b2…",
    "timezone": "Europe/London",
    "schedule": { "rule": "daily", "from": "2027-03-01", "until": "2027-03-21", "times": ["14:30", "19:30"] },
    "dryRun": true
  }'
{
  "timezone": "Europe/London",
  "dates": [
    { "moment": "2027-03-01T14:30", "startsAt": 1803911400000, "name": "Hamlet" },
    { "moment": "2027-03-01T19:30", "startsAt": 1803929400000, "name": "Hamlet" }
  ]
}

Without dryRun, the series is created and a job makes its dates. The answer is 202 straight away; send an Idempotency-Key so a retry gets the same series:

{
  "series": { "seriesKey": "ser_7c1d…", "name": "Hamlet", "state": "creating", "dateCount": 0, "job": { "jobId": "ser_7c1d…:create", "state": "running", "total": 42, "done": 0, "failed": 0 } },
  "jobId": "ser_7c1d…:create"
}

The first date is made from the published floor plan, as POST /v1/events makes an event. Every later date is a copy of the first: the same seats, the same event-owned prices, releases, booking form, online codes and blocked seats. Optional fields: venue, and durationMinutes to set each date’s end.

When the job ends, SeatLayer sends series.created with the event keys it made and any date it could not make. Each date also sends event.created as it is made.

Error When
404 chart_not_found No such floor plan in this organization
422 chart_not_published Publish the floor plan first
422 invalid_schedule The schedule is not valid; detail names the field
422 too_many_dates The schedule makes more than 366 dates
422 invalid_name Empty, over 120 characters, or holds {n}, {date} or {time}

From an event you already set up

Send fromEventKey instead of name and chartKey to turn one event into a run. The event becomes the first date, under its own key, and every other date is a copy of it: its floor plan, prices, checkout, payment account, poster, booking form, online codes and its own blocked seats. A copy carries none of the event’s orders, holds, sold seats, reservations or tickets, and gets its own key. This is how the dashboard’s New event sheet makes several dates: it sets up the first one, then asks for the rest.

{ "fromEventKey": "lighthouse-keeper", "schedule": { "rule": "daily", "from": "2026-11-06", "until": "2026-11-19", "times": ["14:00", "17:30", "20:15"] } }

The series takes the event’s name, zone and floor plan. A moment in the schedule equal to the event’s own start is the event itself, so it is not made again. name and timezone may be sent only when they match the event; chartKey, venue and durationMinutes come from the event and are refused. A dryRun lists every date the series would hold, the event included. series.created lists the event first.

Error When
404 event_not_found No such event in this organization and mode
422 invalid_request A field the event already sets was sent with another value
422 event_has_no_date The event has no start time yet
422 event_in_season The event is a season date
422 event_in_performance_group The event is in an active performance run
422 event_in_series The event is already a date of a series, named by seriesKey (also while that series is still being made); add dates to it instead
422 invalid_schedule The schedule adds no date to the event
503 series_not_started The series did not answer, so whether it started is not known. The event stays with it: send the same request again, with the same Idempotency-Key, to carry on. A request with a new key finds that series, or makes a new one if the first never started
409 create_abandoned This request’s series never started and was given up when another request came for the event. Its key answers this every time: send the request again with a new Idempotency-Key

Follow the job

GET/v1/event-series/:keySecret key

Answers the series, every date made so far in start order (each as retrieveEvent describes it), and the latest job:

{
  "series": { "seriesKey": "ser_7c1d…", "state": "ready", "dateCount": 42, "firstEventKey": "ev_3a9e…" },
  "events": [{ "key": "ev_3a9e…", "name": "Hamlet", "startsAt": 1803911400000, "seriesKey": "ser_7c1d…" }],
  "job": { "jobId": "ser_7c1d…:create", "state": "completed", "total": 42, "done": 42, "failed": 0, "failedDates": [] }
}

state is creating while a job runs, then ready, or partial when some dates could not be made. job.failedDates names each one and why, for example insufficient_credits: every date of the series not made, whichever job it failed in, so a later job does not hide it. Add a failed date again, or send { "retryFailed": true } to add dates, to make it again. The job is durable: if SeatLayer restarts part way, it carries on where it stopped, and no date is made twice.

To list the dates as events, use GET /v1/events?seriesKey=ser_7c1d…. Every event of a series carries seriesKey.

GET/v1/event-seriesSecret key

Lists the series in the key’s mode, newest first (limit up to 100, cursor for the next page).

Add dates

POST/v1/event-series/:key/datesSecret key

Send either a schedule (as above) or exact moments:

{ "dates": ["2027-03-28T19:30", "2027-04-04T19:30"] }

Each new date is a copy of the series’ first date, so a price you changed on it is the price the new dates get. A moment the series already holds is left out; one that failed to be made is made again. { "retryFailed": true } takes every date that failed (up to 100). The answer is 202 {jobId, added}, and series.dates_added says when the job ends. The same Idempotency-Key answers the same job, even when its first answer was lost after the job started. The 366-date cap holds when two calls add dates at once.

Error When
404 series_not_found No such series for this key and mode
409 series_busy A change to every date is running; add the dates once it ends
422 too_many_dates More than 100 new dates, or the series would pass 366
422 invalid_schedule Not valid, or every date is already in the series
422 nothing_to_retry retryFailed, and no date of the series failed

Change all dates

PATCH/v1/event-series/:keySecret key

Change every upcoming date at once. Send any of these:

Field What every upcoming date gets
name The new series name: every upcoming date named Hamlet becomes Hamlet Live
prices {categories}: only the categories, or a tiered category’s tiers, whose price changes, each shaped as PUT /v1/events/:key/pricing takes it. Every other price stays each date’s own
sales on opens sales, off pauses them
{ "name": "Hamlet Live", "prices": { "categories": [{ "key": "std", "price": 42 }] }, "sales": "off" }

Dates already over are left alone. New prices apply to new orders; tickets already sold keep the price they were sold at. A price you leave out keeps each date’s own value, so a run whose weekend dates cost more than its weekdays keeps the difference. A date you renamed on its own keeps its name, but still takes the prices and sales. Opening sales runs each date’s own go-live checks: a date that cannot open is listed in job.failedDates with sales_not_ready, and the rest open. Anything else stays per date, through the Events API.

A job makes the change, one date at a time. The answer is 200 when it ended within the call, else 202; follow it with GET /v1/event-series/:key:

{
  "series": { "seriesKey": "ser_7c1d…", "name": "Hamlet Live", "state": "ready" },
  "jobId": "sju_5e0a…",
  "job": { "jobId": "sju_5e0a…", "state": "running", "total": 41, "done": 12, "skipped": 0, "failed": 0 }
}

The job is durable: a change that SeatLayer had to restart part way carries on, and no date is changed twice. Send an Idempotency-Key, so a retry answers the same job.

Error When
409 series_busy Dates are still being made, or another change is running
422 nothing_to_change No name, prices or sales was sent
422 invalid_name Empty, over 120 characters, or holds {n}, {date} or {time}
422 invalid_sales sales is not on or off
422 invalid_event_pricing No category was sent, one is not on the floor plan or is sent twice, a tier is unknown, a tiered category is given one price, or a price is not valid
Navigation

Type to search…

↑↓ navigate↵ selectEsc close