Express checkout (create + confirm)

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

EventTriggerDescription
checkouts.CheckoutStartedsyncFired when the reservation is placed.
checkouts.CheckoutFulfillmentRegisteredsyncFired when the checkout is confirmed and a fulfillment workflow has been registered.
Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
📘

Eventscheckouts.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 fieldsdate, 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 fieldsemail, 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": "…" }
}
FieldUse
payment_urlRedirect the customer here immediately
checkout_idPoll GET /checkouts/{checkout_id} for status
order_idPersist it — the handle for looking up tickets later
order_display_idHuman-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.

StatustypeWhat to do
400checkout-basket-empty, checkout-basket-line-invalidFix the basket
400checkout-basket-spans-merchants, checkout-basket-spans-providersBasket spans merchants or providers — one checkout per merchant
400checkout-date-in-past, checkout-timeslot-requiredFix the date/time
400checkout-shipping-address-requiredBasket has a physical product; collect the address
400Missing required customer field, or no Idempotency-Key
401A token was sent and rejected as structurally invalid
404campaign-not-foundCampaign not configured — report it to us
409Idempotency collision. See Idempotency
422checkout-fulfillment-request-rejectedThe provider refused the booking for these products or that date
422checkout-product-not-priceable, checkout-product-not-fulfillableOffer another date
422checkout-billing-detail-rejectedDetails rejected — unknown country, invalid e-mail
502checkout-order-failed, checkout-order-empty-redirect, checkout-fulfillment-reservation-failedRetry with the same key
500Server-side fault. Retry with the same key, then quote the correlation id
208Idempotent 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?

ExpressTwo-step
Calls to payment URL12
Customer sees server totals firstNoYes
Knows requires_shipping_address before askingNoYes
Can show a review pageNoYes
Good fitSingle-form purchase, ticket-only catalogReview 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.

Body Params

Single-call create + confirm. The union of the create-checkout basket fields and the confirm-checkout customer/payment fields.

date-time
required

Desired visit date (including timeslot when the product requires one).

boolean
required
Defaults to false

When true the reservation is not tied to a specific date.

string

Caller-supplied reference (e.g. cart ID).

lines
array of objects
required
length ≥ 1
lines*
voucher_codes
array of strings
length ≤ 50

Optional unlock-voucher codes applied at checkout (max 50). Each valid code unlocks one product unit's campaign-defined discount; invalid codes are ignored.

voucher_codes
string
required
string
required
string
required
string

Shipping street name. Required when the basket contains a physical product; ignored otherwise.

string

Shipping house number. Required when the basket contains a physical product; ignored otherwise.

string

Optional house-number addition (e.g. "A", "bis").

string

Shipping postal code. Required when the basket contains a physical product; ignored otherwise.

string

Shipping city. Required when the basket contains a physical product; ignored otherwise.

string

ISO 3166-1 alpha-2 country code of the shipping address (e.g. "NL"). Required when the basket contains a physical product; ignored otherwise.

uri
required

URL the PSP redirects to after payment.

newsletters
array of strings

List of newsletter IDs the customer opted into.

newsletters
Headers
uuid
required

Campaign scope for this request. Filters content to the specified campaign.

uuid
required

Client-generated UUID. Identical keys within the TTL window return the cached response.

string
enum
Defaults to application/json

Generated from available response content types

Allowed:
Responses

Language
Credentials
Header
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json
application/problem+json