Reservations

Temporary holds for tickets - lifetime, refresh mechanics, what happens on expiry. Why reservations exist only for tickets.

A reservation is a temporary hold on a ticket quota - the seats your customer is currently working through in checkout should not be bookable by anyone else, but if the checkout is abandoned the seats need to flow back into the pool. That is exactly what a reservation does.

Reservations exist for tickets (/api/v4/tickets/reservations) and for events (/api/v4/events/reservations). For tours the availability conflict is resolved at order creation or during request handling - there is no dedicated reservations route for tours.

This page covers lifecycle, refresh mechanics and edge cases. Concrete curl examples live on the tickets page under "Create a reservation".

Full endpoint and schema reference: Swagger.

Mental model

A reservation attaches to a ticket plus date/time plus quantity:

TicketReservation
 ├── ticket_id .... which ticket
 ├── start_at ..... when (date for normal, date+slot for time_slot)
 ├── quantity ..... how many seats
 ├── token ........ string, identifies the reservation in PUT/DELETE/order
 └── valid_until .. expiry time

As long as valid_until is in the future, the seats are blocked and show up as taken in capacity / capacities. Once valid_until passes, the seats are automatically available again - you do not have to call DELETE.

Lifecycle

                    POST /reservations
                          │
                          ▼
                    ┌──────────────┐
                    │   valid      │  ◀──── PUT /reservations/:token
                    │              │        extends valid_until
                    │              │
                    └──────┬───────┘
                           │
              ┌────────────┴────────────┐
              ▼                         ▼
    DELETE /reservations/:token    valid_until reached
              │                         │
              ▼                         ▼
       ┌─────────────┐          ┌─────────────┐
       │  released   │          │   expired   │
       │ (immediate) │          │  (passive)  │
       └─────────────┘          └─────────────┘
              │                         │
              └────────────┬────────────┘
                           ▼
              seats back in the available pool

Endpoints

EndpointAction
POST /api/v4/tickets/reservationsCreate a reservation, returns token
PUT /api/v4/tickets/reservations/:tokenChange quantity and extend valid_until
DELETE /api/v4/tickets/reservations/:tokenRelease the reservation, seats free immediately

Note: the endpoints are nested under /tickets/reservations (for ticket reservations) or /events/reservations (for event reservations), not at /reservations directly. Reservations exist as a sub-resource of the parent resource.

Lifetime and refresh

  • Default lifetime: 5 minutes from created_at. Usually enough for a lean checkout (confirm headcount, contact details, payment).
  • Refresh via PUT: each PUT resets valid_until to "now + 5 minutes". You can do this any number of times, but:
  • Hard cap: between created_at and valid_until there can never be more than 60 minutes. A single reservation can therefore live at most one hour, even with many refreshes. Longer checkout flows should detect when they approach the cap and either release the reservation or finalize the booking.

API users with the right permissions can set valid_until explicitly at creation (within the 60-minute cap). Public and shop tokens cannot - they always get the 5-minute default.

What happens at expiry

An expired token returns a validation error on PUT or when you try to use it in an order ("reservation not valid anymore"). There is no automatic resurrection - on expiry you must create a new reservation, which can mean the seats are gone in the meantime.

You do not need to DELETE expired tokens. The server cleans them up.

Reservation in the order

If the reservation is still valid and you create an order, pass the token in the order item:

{
  "order": {
    "items": [
      { "ticket_id": 247, "quantity": 2, "reservation_token": "RESERVATION_TOKEN" }
    ]
  }
}

The server then takes the seats from that reservation, not from the free pool. If the reservation expired in the meantime, order create comes back with a conflict.

Once the order is finalized, the reservation is consumed. No DELETE needed.

Pitfalls

  • The reservation token is the id. For PUT and DELETE use the token string from the create response, not the numeric id.
  • More reservations = more pressure on availability display. Every valid reservation reduces the capacity other customers see. On checkout abandonment, actively call DELETE - otherwise the seats hold for 5 minutes for no reason.
  • Refresh is also a quantity update. A PUT without a body does not refresh - it requires the quantity field, and the lifetime is extended at the same time. To extend lifetime only, send the current quantity again.
  • There is no lookup. You cannot ask the API "is this token still valid?". You only learn it is gone on PUT/DELETE/order-use. So a successful PUT doubles as your health check.
  • Tours have no dedicated reservation route. Tour seats materialize with request confirmation or order finalize. The tickets/reservations pattern does not map to tours - the request mechanism handles the availability conflict instead.
  • 60-minute cap. Long checkouts (login + address + payment + 3DS challenge) can run tight. Once you cross the 50-minute mark, either finalize or release and start again.