Warming up the decks…
Warming up the decks…
BlendPartner APIblendapp.aiReserving 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.
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.
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.
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
}'{
"subtotal": 40,
"discount": 0,
"fee": 2.5,
"vat": 0,
"total": 42.5,
"currency": "USD",
"extras": [],
"refundProtection": null
}| Response field | Type | Description |
|---|---|---|
| subtotal | number | Ticket prices before adjustments. |
| discount | number | Amount removed by discountCode, if any. |
| fee | number | Blend's service fee for this cart. |
| vat | number | Tax applied to the cart. |
| total | number | What the buyer pays. This is the number reserve will return as amount. |
| currency | string | Always USD. |
| extras | array | Priced add-ons included in the cart. |
| refundProtection | object | null | Refund protection pricing when the buyer opted in. |
There are two forms of the reserve call. They take the same body and return the same response.
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.
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 parameter | Type | Description |
|---|---|---|
| buyerName required | string | Full name of the buyer. In a session this is ignored and replaced by the session's buyer. |
| buyerEmail required | string | Where the ticket is sent. In a session this is ignored and replaced by the session's buyer. |
| buyerPhone | string | Optional. In a session this is ignored and replaced by the session's buyer. |
| tickets | array | The cart lines. Use this for multi-line carts, up to 50 lines. |
| selectedTicketId | string | Single-line shorthand: the ticket type, used together with quantity instead of tickets. |
| quantity | integer | How many of selectedTicketId. |
| seatLabels | string[] | For reserved seating: the seats currently held for this buyer. |
| gaSelections | array | General admission selections within a seated event. |
| tableSelections | array | Table selections within a seated event. |
| extras | array | Add-ons to include. |
| discountCode | string | A promo code to apply. |
| showingId | string | For events with multiple showings, which one. |
| refundProtectionOptIn | boolean | Whether the buyer opted into refund protection. |
| accessCode | string | Unlocks hidden ticket types, when the event uses them. |
| successCallbackUrl | string | Where to send the buyer after a successful purchase. |
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
}'{
"success": true,
"data": {
"orderId": "…",
"amount": 42.5,
"currency": "USD"
}
}| Response field | Type | Description |
|---|---|---|
| data.orderId | string | The pending order. You will need it to confirm and to poll status. |
| data.amount | number | The amount to charge, in currency. Charge this exactly; confirm rejects a different amount. |
| data.currency | string | Always USD. |
| data.duplicate | boolean | Present 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. |
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.
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.
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.
Keys are scoped per partner internally, so you cannot collide with another partner's key and they cannot replay yours.
After that a reuse is treated as a new request.
| Outcome | Type | Description |
|---|---|---|
| Same caller, same key | 200 | The original response, with duplicate: true. No new order. |
| KEY_CONFLICT | 409 | A different caller reused the key. |
| DUPLICATE_IN_PROGRESS | 409 | The first request with this key is still being processed. Wait and retry with the same key. |
| Code | Type | Description |
|---|---|---|
| TICKET_SOLD_OUT | 409 | The requested ticket type has no remaining capacity. |
| EVENT_SOLD_OUT | 409 | The event has no remaining capacity. |
| QUANTITY_EXCEEDS_CAPACITY | 409 | The requested quantity is more than the event can supply. |
| TICKET_QUANTITY_EXCEEDS_CAPACITY | 409 | The requested quantity is more than that ticket type can supply. |
| SEAT_CONFLICT | 409 | One or more requested seats are held or booked by someone else. |
| reason: "hold_expired" | 409 | The seat hold ran out. Take a new hold and reserve again. |
| KEY_CONFLICT | 409 | The Idempotency-Key was used by a different caller. |
| DUPLICATE_IN_PROGRESS | 409 | A request with this Idempotency-Key is still in flight. |
Rate limit for checkout: 60 requests per minute.