Draft API. Endpoints may change before general availability.

Checkout, tickets and queues

A checkout session is one buyer's trip through the queue and the payment page. Everything that affects price or stock is decided on our side.

Session lifecycle

queued status
The on-sale is busy. The buyer waits on pay.eventtickets.dev and sees their place in the queue.
open status
The buyer is through. Their tickets are held for 10 minutes while they pay.
complete status
Payment confirmed. An order exists and tickets have been issued.
expired status
The hold ran out or the buyer left. Held stock goes back on sale.

Your colours and wording

Checkout and the confirmation page can match your brand. Set them in Settings › Customisation, with a live preview, or from code with PATCH /v1/branding.

Theme light or dark
Applies to your event, series, checkout, confirmation and ticket pages, and the card form.
Accent colour hex
Main buttons and highlights. Text on it switches between dark and light to stay readable.
Logo and name
Your logo at the top of every page, with or without your organisation name next to it.
Checkout wording
A message under the event name, the button labels, and a link to your terms and conditions.
Confirmation wording
The heading, a message under the buyer's tickets, and an optional button, like Join our mailing list.

PATCH /v1/branding

{
  "checkout": {
    "theme": "dark",
    "accent": "#1D4ED8",
    "text": {
      "checkout_message": "All sales are final. Doors open at 10pm.",
      "pay_button": "Buy now for {amount}",
      "confirmation_heading": "See you on the dance floor."
    }
  }
}

Leave any wording out, or set it to null, to use ours. Prices, the hold timer, the queue and the "Ticketing by EventTickets" badge always show, in your colours.

After payment

Once the payment is confirmed, usually within a few seconds, where the buyer goes depends on the session's success_url:

no success_url default
They see our confirmation page, in your branding: their tickets, Add to Apple Wallet and Save to Google Wallet buttons, and any add-ons. Nothing to build.
success_url url
They go straight to your page instead. Our confirmation page is skipped, so your page is where they see their tickets.

Either way the tickets are emailed too. Put {ORDER_ID} or {CHECKOUT_SESSION_ID} in your success_url and we fill them in when we send the buyer there, so your page knows which order it's for:

POST /v1/checkout_sessions

{
  "event_id": "evt_3f9a1c2b7d4e5f60",
  "quantity": 2,
  "success_url": "https://yoursite.com/thanks?order={ORDER_ID}"
}

Show tickets on your own page

On your server, get the order with GET /v1/orders/:id (your key needs orders:read). Each ticket in it has everything you need for buttons:

url url
The ticket page with its QR code. Link a Show ticket button here.
apple_wallet_url url | null
Adds the ticket to Apple Wallet. Null when Apple Wallet is turned off for the event or the ticket is void.
google_wallet_url url | null
Saves the ticket to Google Wallet. Null when Google Wallet is turned off for the event or the ticket is void.

Your /thanks page, on your server (Node.js)

const orderId = new URL(req.url).searchParams.get("order");
const res = await fetch(`https://api.eventtickets.dev/v1/orders/${encodeURIComponent(orderId)}`, {
  headers: { Authorization: `Bearer ${process.env.EVENTTICKETS_KEY}` },
});
const order = await res.json();

// Ticket links get people in, so only show them to the buyer who paid.
// Save session.id when you create the checkout (in their login session or a cookie) and compare.
if (!res.ok || order.checkout_session_id !== savedCheckoutSessionId) {
  return renderThanks({ tickets: [] }); // "Your tickets are on their way to your inbox."
}

return renderThanks({ tickets: order.tickets });

Your page's HTML, for each ticket

<a href="{ticket.url}">Show ticket</a>

<!-- Only when apple_wallet_url isn't null -->
<a href="{ticket.apple_wallet_url}">
  <img src="/add-to-apple-wallet.svg" alt="Add to Apple Wallet" height="44">
</a>

<!-- Only when google_wallet_url isn't null -->
<a href="{ticket.google_wallet_url}">
  <img src="/save-to-google-wallet.svg" alt="Save to Google Wallet" height="44">
</a>

Link straight to the wallet URLs from the buyer's browser: they build the pass from the ticket as it is now, and the pass stays up to date if the event changes. Use the official badges, unedited, from Apple and Google.

Treat a buyer reaching your success_url as a nice moment, not proof of payment: they might close the tab first. Use the order.paid webhook for anything that has to happen, like sending your own email or updating your records. See Webhooks.

Ticket types

Buyers choose a ticket type: General admission, VIP, or on a multi-day event a Full pass or a Saturday ticket. Each type covers some of the event's days (session_ids). One QR code per ticket works on every day it covers. Pass ticket_type_id when creating a checkout session for an event that sells more than one.

Prices (tiers)

Each ticket type has one or more prices, called tiers in the API, sold in order. The active price is the first one that has not passed its ends_at and still has capacity. When it runs out, the next takes over without you doing anything. The last never expires, so an event only sells out when the event's own capacity is reached.

Each ticket keeps a reference to the price it was bought on and a snapshot of its label and amount, so renaming a price later never changes past sales.

label string
Shown to buyers, for example Early bird.
price integer
Price in the smallest currency unit.
capacity integer | null
How many can sell at this price. Null means no limit.
ends_at timestamp | null
When this price stops selling, even if stock remains.

Add-ons

Add-ons are optional extras, like parking or merch, that buyers add on the checkout page. We set the price and hold the stock while they pay, so the last one can't sell twice. They belong to the order, appear on the ticket page and email, and door staff hand them over when any ticket on the order is scanned.

On-sale queue

Only a set number of buyers can be in checkout for an event at once. Everyone else is queued in the order they arrived and let through as places free up. The queue is enforced on our servers, so it cannot be skipped by editing the page.

By default 15 buyers can be in checkout for an event at once, and each keeps their tickets for 10 minutes while they pay. Contact us if your on-sales need different numbers.

Booking fee

Our fee is per paid ticket. By default the buyer pays it as a separate "Booking fee" line; set an event's fee_payer to organiser to absorb it instead. Each price in the API shows booking_fee, buyer_pays and organiser_receives, so you can display the exact price. Free tickets have no fee. Add-ons carry only the percentage part of the fee, with no fixed part, and discount codes don't apply to them.

Discount codes

Codes can take a percentage or a fixed amount off, apply to one event or all of them, have a usage limit, and require an email domain (for example a student rate for .ac.uk addresses). A code is only counted as used once payment succeeds.

POST /v1/coupons

{
  "code": "STUDENT25",
  "discount_type": "percent",
  "discount_value": 25,
  "event_id": "evt_3f9a1c2b7d4e5f60",
  "max_uses": 200,
  "require_email_domain": "ac.uk"
}

To show a discounted price before checkout, call POST /v1/coupons/preview. It checks the code without using it up.

Pass metadata on a checkout session (for example your own user id or where the buyer came from). It comes back on the order and in its webhooks, so you can match sales back to your own records and marketing.