Checkout walkthrough
Build the full reserve → checkout → redirect flow with the SDK.
This walks through a complete white-label checkout with the SDK. See the checkout flow concept for how the pieces fit together server-side.
Show the event and its tickets
const { data: event } = await client.getEvent('my-event')
const { data: tickets } = await client.listTickets('my-event')
// Only offer purchasable tickets
const purchasable = tickets.filter((t) => t.status === 'AVAILABLE')Each ticket carries live quantityLeft, formatted prices, and per-order limits
(maximumPurchasableTicketCount) to enforce in your UI.
Reserve the cart
Use one stable UUID per attendee as the deviceId (persist it in the browser)
so repeat calls refresh the same hold:
const deviceId = getOrCreateDeviceId() // e.g. crypto.randomUUID() in localStorage
const { data: reservation } = await client.createReservation(
'my-event',
{
items: [{ sku: purchasable[0].id, quantity: 2 }],
promoCode: coupon || undefined,
},
{ deviceId },
)Drive the countdown
clientExpiresAt is the client-facing deadline for the hold:
const msLeft = new Date(reservation.clientExpiresAt).getTime() - Date.now()
startCountdown(msLeft, {
onExpired: () => promptToReserveAgain(),
})Display the priced cart from reservation.items, reservation.total, and
reservation.totalFees. Amounts are integers in the smallest currency unit
(Money).
Create the order
const { data: order } = await client.createOrder(
'my-event',
{
quoteReference: reservation.quoteReference,
email: form.email,
firstName: form.firstName,
lastName: form.lastName,
phone: form.phone,
callbackUrl: 'https://your-site/checkout/callback',
metadata: { seatPreference: 'front' }, // optional, echoed in the TICKET_PURCHASED webhook
},
{ deviceId: reservation.reservationToken },
)Handle the result
if (order.status === 'confirmed') {
// Free order: fulfilled immediately, order.reference is set
showConfirmation(order.reference)
} else {
// Paid order: send the attendee to the payment gateway
window.location.href = order.checkoutUrl
}After payment, the gateway redirects to your callbackUrl with a reference
query parameter. Verify it on your callback page and show the confirmation.
Abandoned carts
Release the hold if the attendee bails out, so the stock frees up immediately:
await client.releaseReservation('my-event', { deviceId })Duplicate orders
Creating a second order for the same reservation returns a 409. Catch the
ApiError and treat it as "payment already in
progress".

