BlendPartner API
blendapp.ai

Getting started

OverviewQuickstartHow the integration worksAuthenticationAny backend

Catalog

Listing eventsEvent detail & ticketsWhich events you see

Checkout

Buyer sessionsInventory & seat holdsReserving an orderTaking the paymentConfirming & settlement

Mobile apps

Native UI (no webview)Embedding the webviewiOS (Swift)Android (Kotlin)React Native

Reference

ErrorsRate limitsTesting & go-live

Reserving an order

Reserving is the step between “the buyer chose tickets” and “the buyer paid”. You send Blend the cart; Blend prices it, creates a pending order, and returns an orderId and the exact amount to charge. You then take the payment on your own rail and confirm to Blend. This page covers the quote, the reserve call, and how to make sure a nervous double-tap never creates two orders.

Step 1. Quote the price (recommended)

POST/v1/channel/events/:id/quote

Send the cart and get back the full price breakdown. Pricing is server-authoritative: discounts, fees and VAT are computed by Blend, and the total you receive here is what reserve will produce for the same cart.

Never compute totals client-side

Do not multiply ticket price by quantity in your app and show that to the buyer. Fees, discount codes, VAT, extras and refund protection all change the total. Show what the quote returns, and charge what reserve returns.

Quote
curl -X POST https://api.blendapp.ai/api/v1/channel/events/EVENT_ID/quote \
  -H "Content-Type: application/json" \
  -H "x-blend-key: ck_live_xxxx" \
  -H "x-blend-secret: cs_live_xxxx" \
  -d '{
    "selectedTicketId": "TICKET_ID",
    "quantity": 2
  }'
200 OK
{
  "subtotal": 40,
  "discount": 0,
  "fee": 2.5,
  "vat": 0,
  "total": 42.5,
  "currency": "USD",
  "extras": [],
  "refundProtection": null
}
Response fieldTypeDescription
subtotalnumberTicket prices before adjustments.
discountnumberAmount removed by discountCode, if any.
feenumberBlend's service fee for this cart.
vatnumberTax applied to the cart.
totalnumberWhat the buyer pays. This is the number reserve will return as amount.
currencystringAlways USD.
extrasarrayPriced add-ons included in the cart.
refundProtectionobject | nullRefund protection pricing when the buyer opted in.

Step 2. Reserve the order

There are two forms of the reserve call. They take the same body and return the same response.

POST/v1/channel/session/events/:id/checkout

Inside a buyer session (the webview). Authenticated by the session cookie. In the embedded checkout this call is made by Blend's own page; you do not call it yourself.

POST/v1/channel/events/:id/checkout

Keyed, server-to-server. Send x-blend-key and x-blend-secret. Use this if you build your own checkout UI and reserve from your backend. Rate limit: 60 per minute.

Body

Body parameterTypeDescription
buyerName
required
stringFull name of the buyer. In a session this is ignored and replaced by the session's buyer.
buyerEmail
required
stringWhere the ticket is sent. In a session this is ignored and replaced by the session's buyer.
buyerPhonestringOptional. In a session this is ignored and replaced by the session's buyer.
ticketsarrayThe cart lines. Use this for multi-line carts, up to 50 lines.
selectedTicketIdstringSingle-line shorthand: the ticket type, used together with quantity instead of tickets.
quantityintegerHow many of selectedTicketId.
seatLabelsstring[]For reserved seating: the seats currently held for this buyer.
gaSelectionsarrayGeneral admission selections within a seated event.
tableSelectionsarrayTable selections within a seated event.
extrasarrayAdd-ons to include.
discountCodestringA promo code to apply.
showingIdstringFor events with multiple showings, which one.
refundProtectionOptInbooleanWhether the buyer opted into refund protection.
accessCodestringUnlocks hidden ticket types, when the event uses them.
successCallbackUrlstringWhere to send the buyer after a successful purchase.
In a session, identity comes from the session

When the call is made inside a buyer session, buyerName, buyerEmail and buyerPhone are overwritten from the session before the order is created. Anything the webview sends for those fields is ignored. See why the buyer's identity cannot be spoofed.

curl -X POST https://api.blendapp.ai/api/v1/channel/events/EVENT_ID/checkout \
  -H "Content-Type: application/json" \
  -H "x-blend-key: ck_live_xxxx" \
  -H "x-blend-secret: cs_live_xxxx" \
  -H "Idempotency-Key: 7d3c9f1e-2b7a-4c1e-9d4b-1f2a3b4c5d6e" \
  -d '{
    "buyerName": "Nour Haddad",
    "buyerEmail": "nour@example.com",
    "buyerPhone": "+96170123456",
    "selectedTicketId": "TICKET_ID",
    "quantity": 2
  }'

Response

200 OK
{
  "success": true,
  "data": {
    "orderId": "…",
    "amount": 42.5,
    "currency": "USD"
  }
}
Response fieldTypeDescription
data.orderIdstringThe pending order. You will need it to confirm and to poll status.
data.amountnumberThe amount to charge, in currency. Charge this exactly; confirm rejects a different amount.
data.currencystringAlways USD.
data.duplicatebooleanPresent and true when this is a replay of an earlier request with the same idempotency key. The rest of the response is the original result.
Reserved is not sold

Inventory is claimed when you confirm, not now. A pending order can still fail at confirm if someone else takes the last ticket first. See when inventory is claimed.

Powered byBlend

Idempotency

Send an Idempotency-Key header on every reserve call. If the same caller sends the same key again, because the network dropped the response, or the buyer double-tapped. Blend returns the original result instead of creating a second order.

  1. Generate one key per buyer tap

    A UUID is fine. Create it the moment the buyer commits, and keep it with that attempt. A retry of the same attempt reuses the key; a new attempt gets a new key.

  2. Blend namespaces the key to you

    Keys are scoped per partner internally, so you cannot collide with another partner's key and they cannot replay yours.

  3. Keys are stored for 24 hours

    After that a reuse is treated as a new request.

OutcomeTypeDescription
Same caller, same key200The original response, with duplicate: true. No new order.
KEY_CONFLICT409A different caller reused the key.
DUPLICATE_IN_PROGRESS409The first request with this key is still being processed. Wait and retry with the same key.

Errors

CodeTypeDescription
TICKET_SOLD_OUT409The requested ticket type has no remaining capacity.
EVENT_SOLD_OUT409The event has no remaining capacity.
QUANTITY_EXCEEDS_CAPACITY409The requested quantity is more than the event can supply.
TICKET_QUANTITY_EXCEEDS_CAPACITY409The requested quantity is more than that ticket type can supply.
SEAT_CONFLICT409One or more requested seats are held or booked by someone else.
reason: "hold_expired"409The seat hold ran out. Take a new hold and reserve again.
KEY_CONFLICT409The Idempotency-Key was used by a different caller.
DUPLICATE_IN_PROGRESS409A request with this Idempotency-Key is still in flight.

Rate limit for checkout: 60 requests per minute.

Next

Taking the paymentHow the webview hands the charge to your native app.Confirming & settlementSign and confirm the order server-to-server; the ticket is issued.Inventory & seat holdsWhy reserve does not claim inventory, and what does.Event detail & ticketsWhere ticket type ids come from.
Powered byBlend© 2026 Blend · Ticketing & event infrastructure