Supports guest users.
Places a reservation and confirms it in a single call, returning a PSP
payment URL directly. Use this when the client submits the basket and
customer details together and does not render an intermediate review
step — it is leaner than POST /checkouts followed by
POST /checkouts/{checkout_id}/confirm because it skips building the
priced review the two-step flow returns from Create.
The request body is the union of the create-checkout basket fields and
the confirm-checkout customer/payment fields. The response carries the
checkout_id (so the client can poll GET /checkouts/{checkout_id} for
status) alongside the order identifiers and payment URL. Supports
idempotency via the Idempotency-Key header.
Events fired
| Event | Trigger | Description |
|---|---|---|
checkouts.CheckoutStarted | sync | Fired when the reservation is placed. |
checkouts.CheckoutFulfillmentRegistered | sync | Fired when the checkout is confirmed and a fulfillment workflow has been registered. |
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Events —checkouts.CheckoutStarted,checkouts.CheckoutFulfillmentRegistered
Why this endpoint exists
The two-step flow exists so you can show the customer a priced review before they commit. If your UI has no review step — the customer fills in one form with basket, date and their details, and expects to land on the payment page — then Create's whole job is to build a priced basket nobody looks at.
Express skips that. One call places the reservation, confirms it, and returns the payment_url. It is leaner by exactly the work the two-step flow does for a review page you are not rendering.
The trade-off is that the customer never sees the server's total before paying. Discounts from wallet, vouchers and membership are applied server-side, so express means the first authoritative price the customer sees is on the payment provider's page. If you display any total on your form, it is your own estimate, and it can disagree with what they are charged. When that matters, use Create + Confirm instead.
Request
The body is the union of the create and confirm bodies.
curl -X POST https://api.acc.funtrips.io/v2/checkouts/express \
-H "x-campaign-id: $CAMPAIGN_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Authorization: Bearer $END_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-09-12T10:30:00Z",
"flexible_date": false,
"lines": [
{ "product_id": "d4b8f2a1-6c3e-4957-b18d-2f5a9c7e3b04", "quantity": 2 }
],
"voucher_codes": ["WELCOME2026"],
"email": "[email protected]",
"first_name": "Sanne",
"last_name": "de Vries",
"return_url": "https://tickets.your-brand.example/order/complete",
"external_reference": "cart-8817"
}'Basket fields — date, flexible_date, lines, voucher_codes, external_reference. Identical semantics to Create, including the rule that date carries a real time for timeslotted products and that invalid voucher codes are silently ignored.
Customer fields — email, first_name, last_name, return_url, newsletters, and the conditional address block. Identical semantics to Confirm.
Required: date, flexible_date, lines, email, first_name, last_name, return_url.
The shipping address problem
requires_shipping_address is computed during Create — which, here, is inside the same call. So you cannot read it before deciding whether to collect an address.
If your catalog can contain physical products, either collect the address up front on the express form, or check physical_product on the products in the basket before submitting. Guess wrong and you get a 400 with checkout-shipping-address-required and have to ask the customer for more details after they thought they were done. For a mixed catalog, the two-step flow is a better fit — it tells you authoritatively before you ask.
Response
200 OK:
{
"data": {
"checkout_id": "7c1b3f9a-2d4e-4a6f-9b1a-3c5e7d2f8a0b",
"order_id": "5e2a7c91-3b6d-4f82-a17c-9d4e6b1f3a25",
"order_display_id": "FT-2026-004471",
"payment_url": "https://checkout.psp.example/pay/abc123"
},
"meta": { "correlation_id": "…" }
}| Field | Use |
|---|---|
payment_url | Redirect the customer here immediately |
checkout_id | Poll GET /checkouts/{checkout_id} for status |
order_id | Persist it — the handle for looking up tickets later |
order_display_id | Human-readable reference |
Note this response carries checkout_id where the two-step Confirm does not — you never saw it, so express hands it back. Store all four before redirecting.
No totals are returned. If you need to show the customer what they paid, read it from GET /checkouts/{checkout_id} after the redirect.
Errors
Express can return anything Create or Confirm can, because it does both.
| Status | type | What to do |
|---|---|---|
400 | checkout-basket-empty, checkout-basket-line-invalid | Fix the basket |
400 | checkout-basket-spans-merchants, checkout-basket-spans-providers | Basket spans merchants or providers — one checkout per merchant |
400 | checkout-date-in-past, checkout-timeslot-required | Fix the date/time |
400 | checkout-shipping-address-required | Basket has a physical product; collect the address |
400 | — | Missing required customer field, or no Idempotency-Key |
401 | — | A token was sent and rejected as structurally invalid |
404 | campaign-not-found | Campaign not configured — report it to us |
409 | — | Idempotency collision. See Idempotency |
422 | checkout-fulfillment-request-rejected | The provider refused the booking for these products or that date |
422 | checkout-product-not-priceable, checkout-product-not-fulfillable | Offer another date |
422 | checkout-billing-detail-rejected | Details rejected — unknown country, invalid e-mail |
502 | checkout-order-failed, checkout-order-empty-redirect, checkout-fulfillment-reservation-failed | Retry with the same key |
500 | — | Server-side fault. Retry with the same key, then quote the correlation id |
208 | — | Idempotent replay. Treat as 200 |
Because reservation and payment happen together, a failure gives you less information about how far it got. The same Idempotency-Key on retry is what makes that safe — it either replays the original success or re-attempts cleanly. Never retry with a fresh key.
Express or two-step?
| Express | Two-step | |
|---|---|---|
| Calls to payment URL | 1 | 2 |
| Customer sees server totals first | No | Yes |
Knows requires_shipping_address before asking | No | Yes |
| Can show a review page | No | Yes |
| Good fit | Single-form purchase, ticket-only catalog | Review step, mixed catalog, loyalty or voucher discounts |
If you are running loyalty or voucher discounts, prefer two-step. Customers who expect a discount and cannot see it applied before paying tend to abandon — or complain afterwards.
