Getting started
Create a key in the staff admin under API and send it with every request:
curl https://tickethero.co.uk/api/v1/products \
-H "Authorization: Bearer th_live_..."Requests and responses are JSON. Money is in pence. Dates are written as YYYY-MM-DD and times as HH:MM in the venue’s local time; timestamps are ISO 8601 in UTC.
Keys and access
A key belongs to one venue and can never reach another venue’s data. There are two kinds:
- Read only keys can use every
GETendpoint. Use these for reporting, your CRM and email tools. - Read and write keys can also create, change, cancel and refund bookings and set capacity. Only give one to a system you trust, and revoke it in the admin if it is ever exposed.
We store only a fingerprint of each key, so a key is shown once, when you create it.
Errors
An error returns a status code and a body you can show to a person:
{ "error": { "code": "full", "message": "10:30 on 2026-12-12 has no places left." } }| Code | Status | Meaning |
|---|---|---|
| unauthorised | 401 | The key is missing or not valid. |
| forbidden | 403 | The key can only read, and this call changes something. |
| not_found | 404 | No product, start time or booking matches. |
| invalid | 400 | Something in the request is wrong. The message says what. |
| full | 409 | That start time has no places left. |
| conflict | 409 | The booking is cancelled or checked in, so it cannot be changed. |
Endpoints
GET/api/v1/products
List products
Your venue and what it sells: each experience with its slug, ticket types, prices by price band, extras and party rules. The slug and the ticket and extra key values are what you send when creating a booking.
GET/api/v1/availability
Read availability
Every start time with the places released, booked, being paid for and still available. Narrow it with from and to.
GET /api/v1/availability?from=2026-12-12&to=2026-12-13
{
"sessions": [
{ "date": "2026-12-12", "time": "10:00", "priceBand": "peak",
"released": 6, "booked": 4, "held": 1, "available": 1 }
]
}PUT/api/v1/availability
Set capacityNeeds a read and write key
Set how many places are on sale for a whole date, or for one start time if you send time. The booking flow on your website updates straight away.
We never go below what is already booked or being paid for, and never above what a start time can hold (maximum). The answer shows what each start time ended up as, so check released rather than assuming your number was used.
PUT /api/v1/availability
{ "date": "2026-12-12", "time": "10:00", "released": 8 }
{
"sessions": [
{ "date": "2026-12-12", "time": "10:00", "released": 8, "maximum": 10,
"booked": 4, "held": 1, "available": 3, "changed": true }
]
}GET/api/v1/bookings
List bookings
Confirmed bookings, newest first. Use since to fetch only bookings confirmed after a moment, and limit for up to 500 at a time (100 if you leave it out). Add status=cancelled to list cancelled bookings instead. more tells you whether there are older ones beyond the limit.
GET /api/v1/bookings?since=2026-12-01T09:00:00Z&limit=50
{ "bookings": [ {
"reference": "TH-7K4Q2M",
"status": "confirmed",
"product": "Santa's Workshop",
"date": "2026-12-12",
"time": "10:30",
"partySize": 4,
"tickets": [
{ "key": "child", "label": "Child (2 to 12)", "quantity": 2,
"unitPence": 2900, "totalPence": 5800 }
],
"extras": [],
"feePence": 150,
"totalPence": 7550,
"currency": "GBP",
"customer": { "firstName": "Sam", "lastName": "Example",
"email": "sam@example.com", "marketingConsent": true },
"source": { "utmSource": "google", "utmMedium": "cpc" },
"guests": [ { "name": "Alex", "age": "6", "interests": "Dinosaurs" } ],
"confirmedAt": "2026-12-01T10:14:03.000Z",
"checkedInAt": null,
"changedAt": null,
"cancelledAt": null,
"cancelReason": null,
"refundedPence": 0,
"refunds": []
} ], "more": false }GET/api/v1/bookings/{reference}
Get a booking
One booking, confirmed or cancelled, in the shape shown above.
POST/api/v1/bookings
Create a bookingNeeds a read and write key
Make a confirmed booking, for example one taken by phone, at the gate or in another system. It takes a place exactly as a website booking does, so two requests can never take the last place.
We work out the price from your products, so you do not send one. Send an Idempotency-Key header with your own unique value: if the same key arrives again, you get the first booking back with status 200 instead of a second booking.
POST /api/v1/bookings
Idempotency-Key: phone-order-10482
{
"product": "santas-workshop",
"date": "2026-12-12",
"time": "10:30",
"quantities": { "child": 2, "adult": 2 },
"extras": { "protection": 1 },
"customer": { "firstName": "Sam", "lastName": "Example",
"email": "sam@example.com" },
"marketingConsent": false
}Returns 201 and the booking. Its source is recorded as api.
PATCH/api/v1/bookings/{reference}
Change a bookingNeeds a read and write key
Move a booking to another date or start time, change the tickets or extras, or correct the customer’s details. Send only what is changing; everything else stays as it is. Moving frees the old place and takes the new one in a single step.
PATCH /api/v1/bookings/TH-7K4Q2M
{ "date": "2026-12-13", "time": "11:00" }
{
"booking": { "reference": "TH-7K4Q2M", "date": "2026-12-13", "...": "..." },
"previousTotalPence": 7550,
"differencePence": -1000
}The price is worked out again. differencePence is positive when the customer now owes more and negative when they are owed money. A cancelled or checked-in booking cannot be changed.
POST/api/v1/bookings/{reference}/cancel
Cancel a bookingNeeds a read and write key
Cancel a booking and put its place back on sale. Add refundPence to record a refund in the same step, or leave the body empty to cancel without one. Cancelling twice is harmless.
POST /api/v1/bookings/TH-7K4Q2M/cancel
{ "refundPence": 7550, "reason": "Illness" }POST/api/v1/bookings/{reference}/refunds
Record a refundNeeds a read and write key
Record a full or part refund without cancelling, for example a goodwill gesture. Refunds add up, and the total can never pass what the booking cost. Returns 201 and the booking with its refunds and refundedPence.
POST /api/v1/bookings/TH-7K4Q2M/refunds
{ "amountPence": 1500, "reason": "Late start" }Webhooks
Add an address in the staff admin under API and we will send it a message for five events: booking.confirmed, booking.changed, booking.cancelled, booking.refunded and booking.checked_in. The body carries the event, your venue and the booking in the shape shown above. Bookings made through the API send the same events as bookings made on your website.
Each request is signed. Check the TicketHero-Signature header against an HMAC of the raw body using your signing secret, and ignore anything that does not match:
import { createHmac, timingSafeEqual } from "node:crypto";
function isFromTicketHero(rawBody, header, secret) {
const expected =
"sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return (
header.length === expected.length &&
timingSafeEqual(Buffer.from(header), Buffer.from(expected))
);
}Answer with any 2xx status within ten seconds. A delivery is tried once; the result of the latest one is shown in the admin, and the bookings endpoint is always there to catch up from.
What to know
- Money does not move through the API yet. A booking you create is recorded as paid for in your own system, and a refund is a record of what you gave back. When card payments arrive, refunds will return the money as well.
- We do not email your customer. Confirmation emails are being built. Until then, tell the customer about a change or cancellation yourself.
- Products and start times are set up with us. Creating experiences, prices and new dates through the API is not available yet.
Need something that is not here? Tell us and we will build it with you. The roadmap shows what is coming.