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

Confirming & settlement

Once you have charged the buyer, your backend tells Blend “this order is paid”. Blend takes the ticket out of inventory, emails it to the buyer, and records that you owe Blend for it. Because this call moves money on paper, it is signed with a key only your server and Blend know, and it is safe to send twice.

Confirm an order

POST/v1/channel/orders/confirm

Keyed, server-to-server only. Send x-blend-key, x-blend-secret and x-blend-signature. Rate limit: 60 requests per minute.

Never call confirm from the app

Confirm needs your secret and your signing key. It belongs on your backend, after your payment rail has reported a successful charge.

Body

Body parameterTypeDescription
orderId
required
stringThe order from the reserve response.
partnerReference
required
stringYour own charge id, the reference your payment rail gave you. Stored with the order for reconciliation.
amount
required
numberWhat you charged, in USD. Must match the order's total within 0.01.

Signature

The x-blend-signature header is a hex-encoded HMAC-SHA256, computed with your signing key (csig_live_xxxx, delivered by email with your other keys) over exactly this string:

Signed string
${orderId}.${amount.toFixed(2)}.USD.${partnerReference}

# Example, for an order of 42.5 USD with your charge id tp_8f21c:
#   <orderId>.42.50.USD.tp_8f21c
  • The amount is formatted with exactly two decimal places (42.5 becomes 42.50).
  • The currency segment is the literal string USD.
  • Four segments joined by ., no whitespace, no trailing newline.
  • The output is lowercase hex.
import { createHmac } from 'node:crypto';

const BLEND_KEY = process.env.BLEND_KEY;                 // ck_live_xxxx
const BLEND_SECRET = process.env.BLEND_SECRET;           // cs_live_xxxx
const BLEND_SIGNING_KEY = process.env.BLEND_SIGNING_KEY; // csig_live_xxxx

export async function confirmBlendOrder(orderId, amount, partnerReference) {
  const payload = `${orderId}.${amount.toFixed(2)}.USD.${partnerReference}`;
  const signature = createHmac('sha256', BLEND_SIGNING_KEY)
    .update(payload)
    .digest('hex');

  const res = await fetch('https://api.blendapp.ai/api/v1/channel/orders/confirm', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-blend-key': BLEND_KEY,
      'x-blend-secret': BLEND_SECRET,
      'x-blend-signature': signature,
    },
    body: JSON.stringify({ orderId, partnerReference, amount }),
  });

  const body = await res.json();

  if (res.status === 503) {
    // ACCRUAL_FAILED_RETRY: safe to retry with the same body and signature.
    throw new RetryableError(body.error?.code);
  }
  if (res.status === 409 && body.error?.code === 'CONFIRM_FAILED') {
    // Inventory was taken between reserve and confirm. Refund the buyer.
    await refundOnYourRail(partnerReference);
    return { refunded: true };
  }
  if (!body.success) throw new Error(body.error?.code ?? 'confirm failed');

  return body.data; // { orderId, status: 'paid', ticket: { . } }
}

Response

200 OK
{
  "success": true,
  "data": {
    "orderId": "…",
    "status": "paid",
    "ticket": {
      "shortId": "…",
      "ticketUrl": "https://…",
      "attendeeName": "Nour Haddad",
      "eventTitle": "…",
      "ticketType": "…",
      "seatLabels": []
    }
  }
}
Response fieldTypeDescription
data.statusstringAlways paid on success.
data.ticket.shortIdstringThe short, human-readable ticket id.
data.ticket.ticketUrlstringA link to the ticket. Safe to show in your app.
data.ticket.attendeeNamestringThe name on the ticket.
data.ticket.eventTitlestringThe event.
data.ticket.ticketTypestringThe ticket type name.
data.ticket.seatLabelsstring[]Seat labels for reserved seating; empty for general admission.

Errors

CodeTypeDescription
INVALID_REQUEST400A required field is missing or malformed.
SIGNING_KEY_NOT_CONFIGURED401Your account has no signing key yet. Contact Blend.
INVALID_SIGNATURE401x-blend-signature does not match. Check the signed string format, the two-decimal amount, and that you are using the current signing key.
NOT_PARTNER_REMIT403The order was not created in partner-remit mode, so there is nothing for you to confirm.
NOT_FOUND404No such order, or the order is not yours.
AMOUNT_MISMATCH409amount differs from the order total by more than 0.01. The response includes { serverTotal, currency }. Do not retry with the server total unless that is what you actually charged.
CONFIRM_FAILED409Blend could not claim inventory, the last ticket was taken between your reserve and this confirm. The response includes { orderId, refundRequired: true }. You must refund the buyer.
ACCRUAL_FAILED_RETRY503Blend could not write the settlement record. Nothing was issued. Safe to retry with the same body and signature.
CONFIRM_FAILED means refund, without exception

When you receive refundRequired: true, the buyer has been charged on your rail for a ticket Blend cannot issue. Refund automatically, from the same code path, and show the buyer that the event sold out. See when inventory is claimed for why this can happen.

Confirm is idempotent

Confirm is guarded by an atomic status transition on the order. If your retry logic sends the same confirm twice, or two workers race to send it, only the first transitions the order to paid. A duplicate does not issue a second ticket and does not send a second email. Retrying after a timeout is the right thing to do.

Powered byBlend

Checking an order's status

POST/v1/channel/orders/status

Keyed, server-to-server. Use it to reconcile, or to recover after a crash between charge and confirm. Rate limit: 300 requests per minute.

Body parameterTypeDescription
orderIdstringLook up by Blend's order id.
externalIdstringLook up by your reference instead. Send one of orderId or externalId.
curl -X POST https://api.blendapp.ai/api/v1/channel/orders/status \
  -H "Content-Type: application/json" \
  -H "x-blend-key: ck_live_xxxx" \
  -H "x-blend-secret: cs_live_xxxx" \
  -d '{ "orderId": "<orderId>" }'
Response fieldTypeDescription
status"paid" | "pending" | "failed"Where the order is.
paidbooleanShorthand for status === "paid".
ticketobjectPresent once the order is paid. Same shape as the confirm response.

Returns 404 NOT_FOUND if the order does not exist or is not yours.

Settlement

Blend is the merchant of record for the ticket; you collected the money. Each confirmed order therefore writes a settlement record: an amount owed by you to Blend for that order. Those records are reconciled on the cycle agreed in your partner contract. The partnerReference you send on confirm is stored alongside the record so that both sides can match line by line.

  1. Confirm writes the record

    If the record cannot be written you get 503 ACCRUAL_FAILED_RETRY and no ticket is issued. Retry.

  2. Reconcile on the agreed cycle

    Match Blend's settlement records against your charges by partnerReference.

Signing key rotation

Blend can rotate your signing key. Rotation invalidates the old key immediately, there is no overlap window, so confirms signed with the old key return 401 INVALID_SIGNATURE. Load the signing key from configuration at request time rather than at process start so a rotation does not require a redeploy.

What the buyer receives

  • A ticket with a signed QR code, delivered by email to the address on the session.
  • An Apple Wallet / Google Wallet pass.
  • ticketUrl, a link to the ticket, which you can show in your app immediately after confirm.

Next

Taking the paymentThe native bridge that precedes this call.Inventory & seat holdsWhy confirm, not reserve, is when inventory is claimed.ErrorsEvery error code in one place.Testing & go-liveExercise the full flow before launch.
Powered byBlend© 2026 Blend · Ticketing & event infrastructure