A booking (event_booking) is the purchase. Tickets (event_ticket) are what it contains. Four tickets bought together are one booking with four tickets, one reference, one payment and one cancellation action.
Purchase rules, in order
The server validates each line of a purchase before it creates anything. The first failing rule is the error the buyer sees.
1. The ticket type is active. isActive !== false.
2. The sale window is open. saleStartDate ≤ now ≤ saleEndDate when set.
3. Capacity remains. capacity − soldCount must cover the quantity.
4. Per-order limit. maxPerOrder, default 10.
5. Per-customer limit. maxPerCustomer, counted across the buyer's existing tickets of that type that are not cancelled or refunded. Free ticket types default to one per customer.
If everything passes, the booking is created with a 9-character upper-case bookingRef. A zero total, or a payment reference already present, marks it isPaid and tickets are issued immediately. Otherwise the booking sits pending until the order is confirmed.
Booking lifecycle
| Status | Meaning |
|---|---|
pending | Created, payment due. No tickets yet. |
paid / confirmed | Payment recorded (or free); tickets issued. |
cancelled | Booking and all its tickets cancelled; buyer notified. |
refunded | Payment returned; counts adjusted. |
expired | A pending booking that was never paid. |
confirm-order is idempotent: confirming a booking that is already paid is rejected rather than issuing a second set of tickets. That is what protects you when a payment webhook and the app both try to confirm.
The public lookup GET /client/events/booking?email=&bookingId= returns a booking to someone who knows both the email and the reference, which is how guest buyers find their tickets without an account.
Ticket statuses
pending → confirmed → checked_in, plus transferred, cancelled and refunded. A ticket's 12-character name is its public ticket id.
QR codes
The QR is not the ticket id. It is a signed payload ticketId:timestamp:signature produced from the ticket's hidden codeSecret, and it is only valid for 60 seconds by default. The attendee app fetches a fresh code (GET /client/events/tickets/:id/qr) when the ticket is opened, and the event can additionally rotate on an interval with qrRotationEnabled.
Every fulfilment and every transfer rotates codeSecret, which is why a forwarded screenshot stops working the moment the real holder does anything with the ticket. Validation rejects a malformed payload, an expired one, a bad signature, and any ticket in cancelled or refunded.
Re-entry is off by default on a ticket type. A valid attendee stepping back in after lunch is denied unless allowReentry is on. See ticket types.
Transfers
The attendee's "Assign to someone" and "Transfer" both call POST /client/events/tickets/:id/transfer with the recipient's email and optional name and reason. The server allows it only when:
- the ticket is
confirmedor alreadytransferred; - the ticket type's
allowTransferis true (default false); transferDeadline, if set, has not passed.
On success the transfer is appended to transfers[], the holder changes, status becomes transferred, the QR rotates, and the recipient is emailed a new code. Claimed perks travel with the ticket when transferPreservesPerks is true (the default).
Comps, stock and activation
Comp (POST /events/tickets/comp) issues tickets with no payment — speakers, staff, sponsors' guests.
Pre-generate (POST /events/tickets/pre-generate) creates pending tickets in bulk and increments soldCount up front, so printed stock is counted against capacity before anyone has bought. Activate (POST /events/tickets/:id/activate) turns one pending ticket into a confirmed one for a named holder, creates a paid booking, adds to revenue and sends the confirmation. Only a pending ticket can be activated.
Fulfilling at an accreditation point
POST /events/tickets/fulfill is the badge desk. The point must exist and be active; a walk-in needs allowWalkIn on that point, an online pickup needs allowOnlinePickup. Cancelled and refunded tickets are refused. Fulfilment rotates the QR, stamps fulfilledAt, fulfilledBy and accreditationPoint, and promotes pending to confirmed.
Will-call is GET /events/tickets/lookup/:eventId — search by name or reference, then fulfil, for people who bought and never opened the app.
Cancel, refund, delete
| Action | Ticket | Counts | Notifies |
|---|---|---|---|
| Cancel ticket | cancelled | No change to sold counts or revenue | event-ticket-cancelled |
| Refund ticket | refunded | soldCount −1 (floored at 0), event totalTicketsSold and totalRevenue reduced | event-ticket-refunded |
| Delete ticket | removed | Stats restored first, then the record goes | — |
| Cancel booking | all tickets cancelled | As cancel | event-order-cancelled |
| Refund booking | all tickets refunded | As refund | event-ticket-refunded |
Cancel when you want the seat released but the money kept; refund when the money goes back. Delete is for mistakes, not for attendees.
Credentials
Physical credentials (event_credential) are wristbands, badges, lanyards, NFC tags, cards and stickers with a code format of barcode, QR, NFC, RFID or manual.
1. Import the codes ahead of time, singly or as a range (credentials/import, credentials/import-range). They start available.
2. Assign a code to a ticket on arrival (credentials/assign). A scan point set to matchBy: credential_code then accepts the wristband instead of the phone.
3. Revoke a lost one individually (credentials/:code/revoke). The ticket is untouched; only that code dies. Other statuses: damaged, lost.
Badges and perks
A badge is generated from a template for a ticket and, once printed, marked badgePrinted so a second badge cannot be issued against the same ticket without a deliberate reset.
Perks (perks[] on the ticket, seeded from the ticket type) are two-step: the attendee claims (perks/:perkId/claim) and staff fulfil (perks/:perkId/fulfill). Each claim records who, when and at which scan point, so drink tokens and meal vouchers reconcile at the end of the day. A perk with quantity exhausted fails the eligibility scan.
Notifications along the way
| Moment | Template |
|---|---|
| Tickets issued or a pending ticket activated | event-ticket-confirmation |
| Ticket cancelled | event-ticket-cancelled |
| Ticket refunded | event-ticket-refunded |
| Booking cancelled | event-order-cancelled |
| Check-in recorded | event-checkin-confirmation |
| Event updated | event-update to every non-cancelled holder |
| 24 h and 1 h before | event-reminder |
Templates are edited in Studio Manager; see notifications.