Skip to main content

An API for AI agents and developers One POST, and the show is on sale.

41 REST endpoints over the whole product: three to get a key, then thirty-eight behind it covering schedules, sub-schedules, events, recurrences, ticket types, sales and refunds, appointment booking, boost requests, 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.

#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 account. 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, 409 when a key is already live or a refund's outcome is not yet confirmed, 422 with the offending fields named, 429 when throttled. A model can branch on that without guessing.

#ledger

The entire surface, on one page.

41 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.

Colour is the verb
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. Twelve filters, including tickets_enabled, is_cancelled and your own external_id.
GET /api/events/{id} One event with its tickets, members and agenda parts.
POST /api/events/{subdomain} Create an event on a schedule, or with an external_id and upsert, update the one you made last time. 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/{id}/cancel Call it off and keep its sales. Optionally email the people registered, with a note.
POST /api/events/{id}/restore Undo a cancellation. Nobody is emailed.
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} mark_paid, cancel or refund. A refund sends Stripe, PayPal or Razorpay money back, in full or an amount you name; an idempotency_key makes a retry safe.
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.
Appointments For an agent that books on a guest's behalf, a voice agent on the phone line most often.
GET /api/schedules/{subdomain}/appointment-types The types a guest can book, with price, host, questions and the next open time.
GET /api/appointment-types/{id}/slots Open times as UTC instants, each with a sentence in the schedule's timezone to read out.
POST /api/appointment-types/{id}/bookings Book one. A taken time answers 409 with the three nearest; an Idempotency-Key makes a retry safe.
Boosts For a network that runs its schedules' boosts under its own Facebook Page and ad account, with the account's own key.
GET /api/boosts Every request on the network's schedules. updated_since keeps a checkout in step without polling each one.
GET /api/boosts/{id} One request: the exact ad and its version, the approval, the payment, the spend.
POST /api/boosts/{id}/approve Approve a named version. A stale version answers 409, so earlier wording can never launch later wording.
POST /api/boosts/{id}/decline Send it back with a note the business reads.
POST /api/boosts/{id}/confirm-payment Record your own Stripe reference and amount. Approved and paid launches it.
DELETE /api/boosts/{id}/confirm-payment Withdraw a payment confirmation before launch.
POST /api/boosts/{id}/stop Pause it at Meta and end it.
GET /api/boost-statements The monthly statements of the fee on actual spend.
GET /api/boost-statements/{period} One month: each campaign's spend, fee and adjustment.

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

#discovery

Four files an agent can read first.

Documentation written for a person is a bad input for a model. These four are written for the model, live at fixed paths, and are served from any installation, hosted or your own.

  • llms.txt 70 lines

    The short one. What Event Schedule is and what each plan includes, links to the main product, audience, comparison and documentation pages, and the API essentials: the auth header, which plan the API needs, the rate limits and a getting-started sequence, so an agent can decide in one fetch whether this API is relevant at all.

  • llms-full.txt 2,221 lines

    The whole reference in one file, so an agent never has to follow a link to finish a task. Every endpoint, every parameter, every error shape.

  • Named multi-step flows: register_and_setup, create_event_with_tickets, sell_tickets, manage_schedule and book_appointment. Each one lists its calls in order, so a plan is data rather than prompt engineering.

  • /api/openapi.json OpenAPI 3.0

    The machine-readable spec. Generate a client, or generate tool definitions, in whatever language your agent is written in.

#calls

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
}

Twelve filters on the events list, including whether tickets or RSVP are switched on, whether an event is cancelled, a venue, a sub-schedule, a date window and the id your own system gave the event. 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. On a Stripe, PayPal or Razorpay sale, refund sends the money back before the status moves, and an amount makes it partial, which leaves the sale paid. 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.

#push

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

Fourteen event types pro
  • sale.created A sale is created, still unpaid.
  • sale.paid Confirmed paid, whether by Stripe, PayPal, Payfast, Razorpay, Invoice Ninja, by hand or free.
  • sale.refunded A paid sale is refunded in full. A partial refund leaves it paid and fires nothing.
  • sale.cancelled A sale is cancelled.
  • installment.paid A payment of an installment plan is collected.
  • installment.failed A scheduled payment could not be collected. Read outcome for why.
  • 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.
#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, by address or as a file

Send a flyer_image_url with the event and the picture is fetched and stored with it, in the same call. A file of your own is multipart, so it gets its own request: 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 or PayPal account, or through Invoice Ninja, Payfast for rand prices, Razorpay for Indian rupee (INR) prices, 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, and a Stripe, PayPal or Razorpay refund goes back through the provider.

stripe paypal payfast razorpay invoiceninja payment_url cash
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.
#who

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
#start

Three requests from nothing to a live page.

  1. 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_...
  2. 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"}
  3. Create events

    POST /api/events/{subdomain}. Ticket types, agenda parts and recurrence go in the same body, so there is no second round trip. On eventschedule.com this one needs the schedule on Pro.

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

Free forever. Upgrade when you're ready.

Free registration for as many people as you like. When you start charging, zero platform fees on every plan - the only deduction is your payment processor's own.

Free
$0 forever, no card

Everything you need to publish a schedule and fill the room, for as long as you like.

  • Unlimited events, sub-schedules and recurring dates
  • Two-way Google, Outlook and CalDAV sync
  • Unlimited free registration and RSVPs, scanned at the door

No expiry, and no card asked for.

Pro Most picked
$5 per month

Start charging, and the rest of the selling kit arrives with it.

  • Sell paid tickets, with the live check-in dashboard
  • Promo codes, add-ons, passes and gift cards
  • The REST API, webhooks, and no Event Schedule branding

7-day free trial, cancel any time.

Enterprise
$15 per month

For a room with a seating chart, a team, and a domain of its own.

  • Reserved seating, drawn once and reused every date
  • Your own domain, and up to five team members
  • Internal and unlisted events, and 1,000 newsletter recipients

Included free on every selfhosted install.

#faq

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.

Which endpoints need the Pro plan, and what happens without it?

On eventschedule.com the list endpoints for schedules, events, sales and feedback return only rows from schedules on Pro, so a free schedule's data is missing rather than refused: check the plan before you read an empty list as nothing there. Reading one schedule, event or sale, updating a schedule, and writing to its events, sub-schedules or sales return 403 with "API usage is limited to Pro accounts". The exception worth planning around is POST /api/sales, which has no plan check of its own: a free schedule can record a sale against a zero-price ticket, while any row with a price on it needs the schedule to be Pro and answers 422 otherwise. On a selfhosted install every one of these checks passes.

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?

41 endpoints across registration, schedules, sub-schedules, events, categories, sales, appointment booking and boost requests, plus two read-only feeds for post-event feedback and fan-submitted content. A network that boosts its schedules under its own Facebook Page approves, declines, records payment for and stops those boosts, and reads its monthly fee statements, with its own key. 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.

Can the API refund a sale?

Yes: PUT /api/sales/{id} with the action refund. On a Stripe, PayPal or Razorpay sale the money goes back through the provider before the status changes. Send an amount to return part of it, and the sale stays paid with its tickets valid; leave it out and the whole remaining balance goes back, the sale becomes refunded, its places return to stock and sale.refunded fires. Send an idempotency_key of your own so a retry returns the first attempt's outcome instead of refunding twice. A refund whose outcome could not be confirmed returns 409 and is never retried for you. Invoice Ninja, Payfast, payment-link and cash sales are only recorded as refunded, so return that money yourself, and a payment plan is refunded in full only.

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 account, so callers that share an address do not share a budget. Requests with no key or a wrong one are 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. Fourteen 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.

#claim

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 is free registration; the API and any ticket with a price on it are $5 a month, and Event Schedule takes nothing from the door.

.eventschedule.com
Start for 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.