Skip to main content
For AI agents, tool builders and developers

One POST, and the show is on sale.

27 REST endpoints over the whole product: schedules, sub-schedules, events, recurrences, ticket types, sales, feedback and fan content. JSON in, JSON out, one header.

An OpenAPI 3.0 spec, llms.txt and agents.json ship with it, so an agent can discover this API and drive it without a human reading the docs first.

POST /api/events/synth-lab X-API-Key
{
  "name": "Analog Night",
  "starts_at": "2026-08-14 20:00:00",
  "duration": 3,
  "tickets_enabled": true,
  "tickets": [{
    "type": "Advance",
    "price": 18,
    "quantity": 120
  }]
}
201 CREATED application/json
{ "data": {
  "id": "Kd3Vq7",
  "url": "https://synth-lab.eventschedule.com/analog-night/Kd3Vq7",
  "tickets": [{ "id": "9pR3vB", "type": "Advance" }]
}, "meta": { "message": "Event created successfully" } }

That one call also writes the event to Google, Outlook or CalDAV if the schedule is connected to one, and fires an event.created webhook.

02 · the contract

Four facts, and you can start writing.

No SDK to install, no OAuth dance, no sandbox to request. The whole surface behaves the same way, which is the only property an agent really needs.

X-API-Key

One header

Register, generate a key in your settings, or log in when you have none. Keys last a year. Nothing else is required.

300 / 30

Requests a minute

300 reads and 30 writes a minute, per IP. Over the line you get a 429, not a silent drop.

per_page ≤ 500

The big lists paginate

100 by default, 500 at most, with a meta block carrying the page, the total and the bounds. Categories and sub-schedules come back whole.

Kd3Vq7

IDs are opaque strings

Never a sequential integer, and an event's is the same string that appears in its public URL, so you can build a link from a response.

2xx the success envelope
{
  "data": [ ... ],
  "meta": { "current_page": 1, "total": 50 }
}
422 field-level errors, always in the same place
{
  "error": "Validation failed",
  "errors": { "starts_at": ["must match Y-m-d H:i:s"] }
}

401 for a bad key, 403 when the plan or the permission is missing, 404, 422 with the offending fields named, 429 when throttled. A model can branch on that without guessing.

03 · the ledger

The entire surface, on one page.

27 endpoints. Not a summary of them, all of them. Everything past /api/login needs the key header, and API access is part of the Pro plan.

Every Event Schedule API endpoint, grouped by resource, with its HTTP method, path and behaviour
Method Path Behaviour
Auth No key required. Each of these has its own throttle.
POST /api/register/send-code Mail a six-digit verification code. Hosted mode only, five per address per hour.
POST /api/register Create the account and return a key. Three per IP per hour.
POST /api/login Mint a key for an account that has none. A live key returns 409 instead.
Schedules A schedule is the tenant: a venue, a talent or a curator.
GET /api/schedules List the schedules you own or administer. Filter by name and by type.
GET /api/schedules/{subdomain} One schedule, with its sub-schedules inlined.
POST /api/schedules Create one. name and type are required; the subdomain is generated from the name.
PUT /api/schedules/{subdomain} Name, contact, description, timezone, language, address. Partial payloads are fine.
DELETE /api/schedules/{subdomain} Marks it deleted, so it and its pages go dark. Owner level only, not admin.
Sub-schedules Named strands inside one schedule, each with its own colour.
GET /api/schedules/{subdomain}/groups id, name, slug and colour for each sub-schedule.
POST /api/schedules/{subdomain}/groups name is required, colour optional. The slug is generated.
PUT /api/schedules/{subdomain}/groups/{group_id} Rename it or recolour it.
DELETE /api/schedules/{subdomain}/groups/{group_id} Events survive; their sub-schedule reference is cleared.
Events The big one. Tickets, agenda parts, members and recurrence all ride along.
GET /api/events Paginated, newest first. Ten filters, including tickets_enabled and rsvp_enabled.
GET /api/events/{id} One event with its tickets, members and agenda parts.
POST /api/events/{subdomain} Create an event on a schedule. Carries its own 30-per-minute throttle.
PUT /api/events/{id} Partial update. Recurrence, tickets and agenda parts survive being omitted.
DELETE /api/events/{id} Delete it, and withdraw it from any synced calendar.
POST /api/events/flyer/{event_id} Multipart upload of a flyer_image for an existing event.
Categories Read-only lookups so you can send a category_id you know exists.
GET /api/categories Every system category, with its id and name.
GET /api/categories/{subdomain} The effective list for one schedule, including its own custom categories.
Sales A sale is a buyer, a set of ticket quantities and a status.
GET /api/sales Filter by event, subdomain, status, buyer email or occurrence date.
GET /api/sales/{id} One sale with its ticket lines.
POST /api/sales Book a sale by hand. Created unpaid; free tickets are marked paid straight away.
PUT /api/sales/{id} Apply an action: mark_paid, refund or cancel.
DELETE /api/sales/{id} Soft delete. It stops appearing in listings.
Feeds Read-only, for pulling audience content onto a site you already run.
GET /api/feedback Post-event star ratings and comments. Filter by minimum rating and date range.
GET /api/fan-content Approved comments, photos and videos. Submitter email addresses are never included.
Colour is the verb GETread POSTcreate PUTupdate DELETEremove

Full documentation for each one, with a cURL example and a response body, is in the API reference.

05 · exchanges

Three calls you will actually write.

Filtering a calendar, standing up a weekly residency, and settling a sale. Everything else is a variation on these.

GET /api/events
?subdomain=synth-lab
&starts_after=2026-08-01
&tickets_enabled=1
&per_page=50
200 OK
"meta": {
  "current_page": 1,
  "last_page": 2,
  "total": 63
}

Ten filters on the events list, including whether tickets or RSVP are switched on, a venue, a sub-schedule and a date window. You narrow server-side rather than pulling a year and filtering in the agent.

POST /api/events/synth-lab
"schedule_type": "recurring",
"recurring_frequency": "weekly",
"days_of_week": "0111110",
"recurring_end_type": "after_events",
"recurring_end_value": "14"
201 CREATED one event, fourteen dates
"schedule_type": "recurring",
"days_of_week": "0111110"

A seven-character mask, Sunday first, so Monday to Friday is "0111110". Frequency is one of daily, weekly, every_n_weeks, monthly_date, monthly_weekday or yearly, and a run can end never, on a date, or after a set number of occurrences.

PUT /api/sales/7bQx2m
{ "action": "mark_paid" }
200 OK
"status": "paid",
"payment_amount": 36,
"total_quantity": 2,
"tickets": [{ "type": "Advance" }]

Three actions, and which ones are legal depends on where the sale is: mark_paid from unpaid, refund from paid, cancel from either. You can also create a sale outright for a buyer who paid you off-platform.

how matching works

You can name a venue or a performer instead of looking up an ID: send venue_name with venue_address1, or members as a list of names and emails, and the API resolves them to schedules on your account. To be exact about what that is: it matches an existing schedule you own or follow, and returns a 422 naming the one it could not find. It does not invent a venue for you. Categories work the same way: send category as a name and it is matched against that schedule's category list.

06 · webhooks

Or stop asking, and get told.

Polling a sales endpoint every minute is a waste of both our time. Register an endpoint and the traffic reverses: we POST to you, signed, the moment something happens.

POST https://your-app.example/hooks
X-Webhook-Event: sale.paid
X-Webhook-Signature: sha256=<hex>
X-Webhook-Timestamp: 2026-08-14T20:11:04+00:00
User-Agent: EventSchedule-Webhook/1.0

{ "event": "sale.paid", "data": { ... } }

The signature is an HMAC-SHA256 of the raw body, keyed on a secret shown once when you add the hook. Verify it before you trust the payload. Key on data.id plus the event type, because a delivery can repeat and one sale fires several types. There is a delivery log in your settings when something goes wrong.

Webhook reference, with verification snippets

Twelve event types pro
  • sale.created A sale is created, still unpaid.
  • sale.paid Confirmed paid, whether by Stripe, Invoice Ninja, by hand or free.
  • sale.refunded A paid sale is refunded.
  • sale.cancelled A sale is cancelled.
  • event.created A new event exists.
  • event.updated An event changed.
  • event.deleted An event is gone.
  • event.cancelled An event is cancelled.
  • ticket.scanned A ticket QR code is scanned at the door.
  • ticket.booked A pass holder reserves a place on a date.
  • ticket.booking_cancelled A pass holder releases a reserved place.
  • feedback.submitted An attendee left a rating.
07 · the rest

The parts that make it safe to automate.

Details that only matter once your code is running unattended, which is exactly when they matter most.

free and pro open source

Your own install, same API

Event Schedule is open source, and the API does not change when you host it yourself: same routes, same OpenAPI spec, same discovery files, served from your own domain. On a selfhosted install the Pro gate returns true unconditionally, so no endpoint is held back and no key talks to anyone else's server.

docker or bare metal your database no outbound calls required

How selfhosting works

pro

Flyers, as a second call

Artwork is multipart, so it gets its own request. Create the event, then POST a flyer_image to the flyer endpoint with the returned ID.

curl -X POST .../api/events/flyer/Kd3Vq7 \
  -H "X-API-Key: $KEY" \
  -F "[email protected]"
free and pro

Languages, made explicit

Set language_code on a schedule and its pages are served in that language; twelve are supported. A schedule can also nominate one translation target, and its own copy is machine-translated into it on a scheduled pass.

ar de en es et fr he it nl pt ro ru
pro

Money, without a middleman

Ticket types created through the API sell through your own Stripe account, or through Invoice Ninja, a payment URL, or by hand. Event Schedule takes zero platform fees on ticket sales: the only deduction is your processor's. Sales come back through the sales endpoints and through sale.paid webhooks, with the ticket lines attached.

stripe invoiceninja payment_url manual
pro

Read-only feeds

Two endpoints exist purely so you can pull audience content somewhere else: post-event ratings and comments, and approved fan photos, videos and comments. Fan submissions carry a display name only; the ratings feed names the attendee, so treat it as owner-facing.

Each kind of fan submission has its own ID sequence, so key on type and id together when you store a row.

pro

Partial writes that keep their nerve

PUT takes the same body as create and applies only what you send. Recurrence configuration, ticket types and agenda parts are preserved when they are absent, so an agent that only knows the new start time cannot quietly erase a run's ticket tiers. Every write is scoped to the schedules the key's owner owns or administers, and anything outside that returns 403 rather than silently doing nothing.

401

Key missing, wrong or expired.

403

Not your schedule, or the plan does not cover it.

429

Throttled. Back off and retry.

08 · callers

What people point at this API.

An HTTP API has no opinion about what is calling it, which is the point.

AI Assistants

Turn a conversation into a published event. Register, create the schedule and create the event in three calls, then hand back the URL from the response.

Learn more

Developer Tools & Scripts

A cron job, a CLI, a one-off migration. Generate a client from the OpenAPI spec and the whole surface is typed for you.

Learn more

Community Bots

A Discord, Slack or Telegram bot that creates the event when someone announces it in the channel, and posts the ticket link back.

Learn more

Booking Platforms

Keep your own front end and let Event Schedule hold the events, the ticket types and the sales. Webhooks push each paid sale straight back to you.

Learn more

Calendar Aggregators

Pull a date window with the events filters, or take the iCal feed and skip the API entirely. Both come off the same schedule.

Custom Integrations

Anything that speaks HTTP and JSON. If you would rather not write the client, the OpenAPI spec will write it for you.

Learn more
09 · first run

Three requests from nothing to a live page.

01

Get a key

One unauthenticated POST to /api/register, or to /api/register/send-code first in hosted mode. The response body carries the key and its expiry.

X-API-Key: es_live_...
02

Create a schedule

POST /api/schedules with a name and a type. The subdomain is generated from the name and the public page exists immediately.

{"name": "Synth Lab", "type": "venue"}
03

Create events

POST /api/events/{subdomain}. Ticket types, agenda parts and recurrence go in the same body, so there is no second round trip.

{"name": "Analog Night", "duration": 3}

Free forever. Upgrade when you're ready.

$0
Scheduling, calendar sync, newsletters, and 25 tickets a month
$5/mo
Unlimited tickets, QR check-in, event graphics, and no branding

Zero platform fees on ticket sales. You only pay Stripe's processing fee.

See all plans
10 · questions

Frequently asked questions

What developers ask before they write the first request.

Is the API free to use?

The REST API is part of the Pro plan at $5 a month, with a seven-day trial when you subscribe. Selfhosted installations are Pro by definition, so running your own copy unlocks every endpoint at no cost. Ticket sales carry zero platform fees on every plan and in both modes: you keep everything except your payment processor's cut.

How does authentication work?

One header, X-API-Key. Get a key from POST /api/register or POST /api/login, or generate one in your account settings. Keys are valid for a year. Login only mints a key when the account has none, and returns 409 while one is still live, so store the key rather than calling login on every run. Accounts with two-factor authentication have to generate keys from the web UI. Every endpoint except register, send-code and login requires the header.

What can I actually do with it?

27 endpoints across registration, schedules, sub-schedules, events, categories and sales, plus two read-only feeds for post-event feedback and fan-submitted content. Schedules, sub-schedules, events and sales have full create, read, update and delete; categories are read-only lookups. A single create call can carry ticket types, agenda parts, performing members, a venue and a recurrence pattern, so publishing a run of shows is one request rather than six.

What is llms.txt, and why are there two of them?

llms.txt is an emerging convention for telling a language model what a site is and where its documentation lives. Event Schedule publishes both: llms.txt is a short routing summary an agent can read to decide whether this API is relevant, and llms-full.txt is the entire reference in one file, so an agent that has decided to proceed never needs to follow a link.

What are the rate limits?

300 GET requests a minute and 30 POST, PUT or DELETE requests a minute, counted per IP address. Creating an event carries its own 30-per-minute throttle on top of that, and the auth endpoints have their own tighter limits. Going over returns 429 with an error body.

Are there webhooks, or do I have to poll?

There are webhooks, on the Pro plan. Twelve event types cover sales, event changes, door scans, pass bookings and feedback. Each delivery is signed with HMAC-SHA256 in an X-Webhook-Signature header so you can verify it came from us, payloads match the shapes the API returns, and there is a delivery log in your settings for debugging.

Which IDs does the API use?

Encoded strings, never sequential integers. An event, a ticket, a sale and a sub-schedule all identify themselves with a short opaque string, and an event's is the same string that appears in its public URL, so you can build a link straight from a response. Category IDs are the exception: they are small integers you read from the categories endpoint.

Can I run this against my own installation?

Yes. Event Schedule is open source and the API is the same in both modes: same routes, same spec, same discovery files served from your own domain. On a selfhosted install the Pro gate returns true unconditionally, so nothing is held back.

11 · your key

The last call is the first one.

Pick a name and start, or register straight from your code. Publishing a schedule and its dates is free forever, and so are the first 25 paid tickets a month; the API and unlimited ticket sales are $5 a month, and Event Schedule takes nothing from the door.

.eventschedule.com
Get started free

No card to start. Or go straight to the API reference and the OpenAPI spec.

POST /api/register no key required
{
  "name": "Your Agent",
  "email": "[email protected]",
  "password": "..."
}
201 CREATED
{ "data": {
  "api_key": "your_new_api_key",
  "api_key_expires_at": "2027-07-30T00:00:00Z"
} }

In hosted mode, POST /api/register/send-code mails a six-digit code first, and you pass it as verification_code.