Skip to API documentation

Current version: 2026-08-26

SB Checkout API v1 documentation

Build server-side integrations with isolated TEST and LIVE credentials, server-owned prices, idempotent writes and signed asynchronous events.

Quickstart

Create a TEST key in the Checkout developer area, keep it only on your backend, then create a hosted session with a unique idempotency key.

  1. Configure the business and receiving currency in Business. Create a TEST store, product and active offer. Generate a TEST key for the same store with checkout.sessions:create, checkout.sessions:read and checkout.orders:read, checkout.payment_methods:read. An active store does not confirm charging availability.
  2. SB_CHECKOUT_HOST is the SB Checkout platform domain, not the merchant domain. Use the returned offerPublicToken and query /payment-methods for the currency and key environment. Keep secret keys on the server only.
  3. The example omits successUrl: use the returned checkoutUrl for the canonical page. A custom return route must exist and satisfy the destination policy. Confirm the result via the API or a signed webhook, never the redirect. Set recoveryConsent to ACCEPT only after the buyer expressly agrees.
cURL
curl --request POST 'https://SB_CHECKOUT_HOST/api/checkout/v1/sessions' \
  --header 'Authorization: Bearer sbck_test_REDACTED' \
  --header 'SB-Checkout-Version: 2026-08-26' \
  --header 'Idempotency-Key: order_20260825_0001' \
  --header 'Content-Type: application/json' \
  --data '{
    "offerPublicToken": "offer_public_token",
    "quantity": 1,
    "customer": {
      "firstName": "Ada",
      "lastName": "Lovelace",
      "email": "ada@example.com",
      "phone": "+351912345678",
      "country": "PT"
    },
    "paymentMethodRequested": "SB_PAY",
    "recoveryConsent": {
      "version": "checkout-recovery-consent-v1",
      "action": "DECLINE",
      "channels": { "email": false, "sms": false, "whatsapp": false }
    }
  }'

Hosted or custom checkout

Use hosted sessions when SB Checkout renders the purchase. Use Payment Intents when your site renders the experience. Both use server-owned prices, the same order source of truth, consented affiliate attribution and signed events.

Hosted checkout

Create a session on your backend and redirect the buyer to the returned checkout URL.

Custom checkout

Create a Payment Intent on your backend, then use only its origin-bound client secret with the browser SDK.

JavaScript
// Trusted merchant backend
const response = await fetch("https://SB_CHECKOUT_HOST/api/checkout/v1/payment-intents", {
  method: "POST",
  headers: {
    Authorization: "Bearer sbck_test_REDACTED",
    "SB-Checkout-Version": "2026-08-26",
    "Idempotency-Key": "cart_8f31_payment_intent",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    offerPublicToken: "offer_public_token",
    customer: { firstName: "Ada", lastName: "Lovelace", email: "ada@example.com" },
    allowedOrigins: ["https://shop.example.com"],
  }),
});
if (!response.ok) {
  throw new Error(`Payment Intent failed (request ${response.headers.get("X-Request-Id")})`);
}
const { paymentIntent } = await response.json();

// Return only paymentIntent.clientSecret to the exact allowed browser origin.
// Browser: after loading /checkout/sdk/v1/sb-checkout.js; never expose sbck_*.
const checkout = new SBCheckout({
  clientSecret: paymentIntent.clientSecret,
  apiBase: "https://SB_CHECKOUT_HOST",
});
const methods = await checkout.listPaymentMethods();
if (!methods.length) throw new Error("No payment methods are available");
const intent = await checkout.confirmPayment({
  paymentMethod: methods[0].type,
  idempotencyKey: SBCheckout.createIdempotencyKey("cart_8f31"),
});
HTML · SRI
<script
  src="https://SB_CHECKOUT_HOST/checkout/sdk/v1/sb-checkout.js"
  integrity="sha384-WpLaTxPMdY5dPRTN1YDIFP3QH7pTtyo54r+aRKWjvphJc7Wpb+R3hhby8hCgGzPD"
  crossorigin="anonymous"
></script>

Contract and security

Authentication

Authorization: Bearer sbck_test_*

Client authentication

Origin-bound payment intent client_secret

Version

SB-Checkout-Version: 2026-08-26

Idempotency

Idempotency-Key on creating POST commands

Correlation

X-Request-Id on every response

Pagination

Query parameters: page, limit <= 100, search and status

Source of truth

Order state plus signed webhook events

Never expose a secret key in browser code, mobile applications, screenshots, logs or public repositories.

Resources

ResourceEndpoints
StoresGET /stores · POST /stores · GET/PATCH /stores/{id}
ProductsGET /products · POST /products · GET/PATCH /products/{id}
OffersGET /offers · POST /offers · GET/PATCH /offers/{id}
CustomersGET /customers · POST /customers · GET/PATCH /customers/{id}
SessionsPOST /sessions · GET /sessions/{id}
OrdersGET /orders · GET /orders/{orderTokenOrNumber}
Payment methodsGET /payment-methods
Payment intentsGET/POST /payment-intents · GET /payment-intents/{id} · POST /payment-intents/{id}/confirm · POST /payment-intents/{id}/cancel · POST /payment-intents/{id}/capture
RefundsGET/POST /payment-intents/{id}/refunds · GET/POST /orders/{orderTokenOrNumber}/refunds
DisputesPOST /payment-intents/{id}/disputes · GET /disputes · POST /disputes/{id}/resolve
SettlementsGET /settlements
PayoutsPOST /settlements/{id}/payouts
Payment linksGET /payment-links · POST /payment-links · GET /payment-links/{id} · POST /payment-links/{id}/deactivate
WebhooksGET/POST /webhook-endpoints · GET/PATCH /webhook-endpoints/{id} · POST /webhook-endpoints/{id}/rotate-secret · GET /webhook-deliveries

Payment methods

Discover available payment methods through the API. Availability depends on the currency, country and the payment methods enabled for the account.

SB Pay

Internal SB payment adapter. Lifecycle capabilities vary by TEST or LIVE mode and must be read from the descriptor.

Pix

Pix payments in BRL. Confirmation returns a DISPLAY_PIX_QR action; keep the same intent and poll until a terminal state.

Pix TEST is simulated and does not move funds. LIVE requires confirmed payment reconciliation; scanning a QR code is not payment proof.

Payment lifecycle

Capture, refund, dispute and payout commands are server-only, idempotent and capability-gated by credential mode and payment adapter.

OperationTESTLIVE
Manual captureAvailableAdapter dependent
Full and partial refundsAvailableAdapter dependent
Dispute simulation and resolutionAvailableAdapter dependent
Payout simulationAvailableAdapter dependent

The current SB Pay LIVE adapter advertises none of these lifecycle commands. Unsupported requests return HTTP 422 with a stable capability code before financial state changes.

JavaScript
// Trusted merchant backend only: never expose sbck_* in the browser.
const response = await fetch(
  "https://SB_CHECKOUT_HOST/api/checkout/v1/payment-intents/sbpi_test_ID/refunds",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer sbck_test_REDACTED",
      "SB-Checkout-Version": "2026-08-26",
      "Idempotency-Key": "refund_order_1042_attempt_1",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ amount: "10.50", reason: "customer_request" }),
  }
);
const body = await response.json();
if (!response.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.refund.id, body.refund.status);

Webhooks

Verify the exact raw request bytes, reject stale timestamps and deduplicate by event.id. During secret rotation, accept current and previous signatures only for the declared overlap window.

Node.js
import crypto from "node:crypto";

export function verify(rawBody, timestamp, signature, secret) {
  if (!(Buffer.isBuffer(rawBody) || typeof rawBody === "string")) return false;
  if (typeof timestamp !== "string" || !/^(?:\d{10}|\d{13})$/.test(timestamp)) return false;
  if (typeof signature !== "string" || signature.length > 2048) return false;
  if (typeof secret !== "string" || !secret) return false;

  // Current wire format: Unix milliseconds. Accept legacy Unix seconds too.
  // Keep the original timestamp text unchanged in the signed input.
  const sentAt = Number(timestamp) * (timestamp.length === 10 ? 1000 : 1);
  if (Math.abs(Date.now() - sentAt) > 300_000) return false;

  const parts = signature.split(",").map((part) => part.trim());
  if (parts.length > 16) return false;
  const signatures = parts.filter((part) => part.startsWith("v1="));
  if (!signatures.length || signatures.some((part) => !/^v1=[a-fA-F0-9]{64}$/.test(part))) return false;

  const expected = crypto.createHmac("sha256", secret)
    .update(timestamp + ".", "utf8")
    .update(rawBody) // raw Buffer from the HTTP body, before JSON parsing
    .digest();
  return signatures.some((part) => crypto.timingSafeEqual(
    expected, Buffer.from(part.slice(3), "hex")
  ));
}

Errors and retries

  • 202 processing or ambiguous provider result; retrieve the same Payment Intent and do not create a second attempt
  • 400 invalid request or unsupported API version
  • 401/403 invalid credential, mode or missing scope
  • 409 idempotency conflict or stale optimistic version
  • 429 rate limited; honor Retry-After with exponential backoff and jitter
  • 422 operation not supported by the selected payment adapter or payout rail
  • 5xx temporary server failure; retry only idempotent requests

For PATCH requests, send ifVersion from the latest resource response. A stale version returns HTTP 409 and never overwrites the newer change.

Operational boundary

TEST catalog and hosted checkout resources may be available while LIVE financial execution, outbound webhook delivery or digital delivery remain disabled by operational flags. Never infer payment or settlement from a redirect, and never treat TEST lifecycle simulation as provider-backed money movement.