Checkout
Confirm the checkout
Consumes the session and writes everything, transactionally: the customer (created, or your existingCustomerId reused), the consent evidence (recorded against the SESSION's pinned versions with PARTNER_ASSERTED provenance), the subscription, and the first charge. Synchronous — the subscription id is in the response.
x-api-idempotency-key is REQUIRED (400 IDEMPOTENCY_KEY_REQUIRED without it). A retry with the same key replays the full original response; a concurrent duplicate answers 409 IDEMPOTENT_REQUEST_IN_PROGRESS; the same key reused for a DIFFERENT session answers 422 IDEMPOTENCY_KEY_REUSED.
Consent is assertion (you presented the pinned documents in your UI): termsAccepted: true is required, and recurringConsentAccepted: true on the CARD rail. On CARD, confirm requires the session's card to be CARD_READY — or a cardHandoffToken carried from an expired session.
Refusals to build the retry loop around: a 410 means the pinned plan/terms changed or the session expired (re-mint); PORTAL_CHECKOUT_AUTHORIZED_AMOUNT_CHANGED means the engine re-run no longer matches the pinned amount (card-release → re-quote → re-mint → re-confirm).
Invoice rail: the first invoice is billed asynchronously (billingPending: true). A tenant that serves no hosted portal sends invoice emails WITHOUT an online pay-now link — payment instructions ride the invoice document itself.
Required Scope
checkout
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Confirm the checkout › Request Body
sessionIdThe session to confirm. Single-use: consumed whatever the outcome.
paymentMethodThe payment rail the buyer chose.
termsAcceptedYour assertion that the buyer accepted the pinned terms on your surface. Must be true — false/absent refuses with PORTAL_CHECKOUT_CONSENT_REQUIRED.
acceptedAtWhen the buyer accepted (ISO 8601). Evidence, not authority — implausible instants (future, or older than an hour) are replaced with the server’s own clock.
recurringConsentAcceptedYour assertion that the buyer accepted recurring billing. REQUIRED true on the CARD rail.
cardHandoffTokenA card handoff token carried from an EXPIRED session (the edit-after-card loop) — the captured card survives into this confirm. Defaults to the card window this session opened.
Confirm the checkout › Responses
subscriptionIdThe created subscription.
referenceYour reference, echoed.
billingPendingTrue when the first invoice is billed asynchronously after this response (INVOICE rail, and CARD confirms whose charge is queued). Listen for subscription.billed / poll the subscription.
Quote a first period
Pure read: first-period pricing including exact VAT for the buyer you describe, the terms bodies and recurring-billing disclosure context, start-date rules, allowedPaymentMethods, and cardPaymentsEnabled — which is chargeability-aware, so a card step is never offered in a currency the window would refuse. firstPeriodChargeable: false means a zero first period (free trial / 100% discount): adapt your copy ("no charge today"); the card window still opens save-only.
country is required so VAT resolves exactly; consumer-facing prices MUST be displayed including VAT (render the gross figures). A vatNumber is accepted for BUSINESS buyers only and is checked against the country's published format.
A STRIPE_CROSS_CURRENCY_NOT_ENABLED refusal later in the flow is a configuration issue on your Stripe account (enable cross-currency charges or add a bank account in that currency on Stripe), never a buyer error — cardPaymentsEnabled: false here is how you avoid offering that dead end.
Required Scope
checkout
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Quote a first period › Request Body
planVersionIdThe plan version to quote (from the catalogue).
planItemIdThe pricing-card item on that version.
countryBuyer country, ISO 3166-1 alpha-2 — REQUIRED so VAT resolves exactly.
customerTypeWhether the buyer is a business or a private person.
vatNumberThe buyer’s VAT number (BUSINESS only — refused otherwise).
startDateRequested start date (YYYY-MM-DD). Defaults to today; never in the past.
currencyPresentment currency (ISO 4217). Must be in the card’s availableCurrencies; refused with PORTAL_CHECKOUT_CURRENCY_NOT_AVAILABLE otherwise. Absent → the card’s own currency.
Buyer-chosen quantities for quantity-bearing products.
languageLanguage for display texts, terms and the disclosure.
Quote a first period › Responses
quoteFirst-period quote — the SAME payload the hosted portal renders (display parity is structural): plan title/description + display lines, pricing (subtotal, per-rate VAT breakdown, total, currency), quantity rows, terms bodies, the resolved start date, allowed payment methods and cardPaymentsEnabled.
firstPeriodChargeableFalse when the first period nets to zero (free trial / 100% discount). The card window still opens save-only — the card is stored for the first real charge and nothing is authorized ("no charge today").
Integration readiness
One object, a machine-readable state per tenant precondition — the "am I integrated?" preflight, replacing error-by-error discovery. Make this the first call of your integration.
Preconditions for a working checkout: the Subscription license, at least one published plan with a sellable item, and — for the card rail — an active Stripe Connect account. Hosted card windows additionally need your return origin on your embeddableOrigins allowlist. LedgerBee-hosted terms are NOT a precondition: generalTermsPublished is informational — when you host terms in LedgerBee they ride the quote/session and confirm records per-version acceptance; when you don't, you host your own terms and confirm records your acceptance assertion.
Customer groups are NOT a readiness item: they are per-request data. Discover them via GET /v1/customers/groups.
Required Scope
checkout
Integration readiness › Responses
subscriptionLicenseWhether you hold the Subscription license — without it nothing is checkoutable.
publishedPlansNumber of published plans.
checkoutablePlansNumber of published plans with a sellable item — checkoutable on this surface.
generalTermsPublishedInformational, never a gate: whether published GENERAL terms exist in LedgerBee. When true, quote and session responses carry them and confirm records per-version acceptance evidence; when false, you host your own terms and confirm records your acceptance assertion.
embeddableOriginsThe configured embeddableOrigins allowlist — required for HOSTED card windows’ returnUrl.
Mint a checkout session
Mints the single-use session the rest of the flow runs against, PINNING what your buyer was shown: the plan version, the terms + disclosure versions, the resolved pricing and currency, quantities, VAT inputs and start date. Confirm re-runs the billing engine against those pinned inputs and refuses a drifted amount — you never bill a number the engine wouldn't produce, and a tenant republish affects new sessions only.
The buyer is plain request data — no tokens, no buyer verification. customerGroupId is REQUIRED (discover groups via GET /v1/customers/groups). Pass existingCustomerId for a returning buyer so they keep one customer record. Pass reference (your order/cart id) and every checkout.* webhook echoes it back.
The session expires after expiresInMinutes (default 30). An expired session answers 410 — re-mint and, if a card was already captured, carry its cardHandoffToken into the fresh session's confirm so the buyer never re-enters card details.
Required Scope
checkout
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Mint a checkout session › Request Body
planVersionIdThe plan version to check out (from the catalogue).
planItemIdThe pricing-card item on that version.
The buyer — plain request data; no tokens, no ceremony.
startDateRequested start date (YYYY-MM-DD). Defaults to today; never in the past.
currencyPresentment currency (ISO 4217); must be in the card’s availableCurrencies.
Buyer-chosen quantities.
expiresInMinutesSession TTL in minutes. Default 30.
referenceYour own order/cart id (Stripe client_reference_id parity) — echoed on session reads, the confirm response and every checkout.* webhook payload.
languageLanguage for display texts, terms and the disclosure.
Mint a checkout session › Responses
sessionIdThe opaque session id — your handle for every later call.
expiresAtWhen the session expires (ISO 8601). Expired sessions answer 410.
referenceYour reference, echoed.
quoteThe same first-period quote payload POST /v1/checkout/quote returns.
What the session pinned — render exactly this.
firstPeriodChargeableFalse when the pinned first period nets to zero (save-only card window).
Rehydrate a session
PEEK a live session after a partner-side reload: the buyer payload, the pinned quote summary, your reference, and whether a card window was opened. 410 when expired or already consumed.
Required Scope
checkout
path Parameters
idThe opaque session id (64 hex chars).
Rehydrate a session › Responses
sessionIdThe opaque session id.
expiresAtWhen the session expires (ISO 8601).
referenceYour reference, echoed.
planVersionIdThe pinned plan version.
planItemIdThe pinned pricing-card item.
buyerThe buyer payload the session was minted with.
firstPeriodChargeableFalse when the pinned first period nets to zero.
cardWindowOpenedWhether a card window has been opened on this session.
Release the card hold (edit-after-card)
The edit-after-card loop's first step: when the buyer changes quantities, start date or VAT inputs AFTER the card step, confirm refuses the now-stale pinned amount — call this, then re-quote, re-mint and re-confirm.
SAVE_ONLY: the captured card SURVIVES (carry its cardHandoffToken into the fresh session's confirm — the buyer never re-enters card details; the first charge is sized at billing). RECAPTURE_REQUIRED: the card could not be preserved — discard it and open a fresh card window.
Required Scope
checkout
path Parameters
idThe opaque session id (64 hex chars).
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Release the card hold (edit-after-card) › Responses
statusSAVE_ONLY: the card survives — re-quote, re-mint and confirm; the first charge is sized at billing. RECAPTURE_REQUIRED: discard the card and open a fresh window.
Poll card status
Poll after the buyer completes the card form: PENDING → keep polling, CARD_READY → confirm may proceed (the captured card's display details are included — show the buyer WHICH card will be charged), EXPIRED → the window lapsed, open a fresh one. The checkout.card_ready webhook is the push alternative; this poll stays authoritative.
Required Scope
checkout
path Parameters
idThe opaque session id (64 hex chars).
Poll card status › Responses
statusPENDING: keep polling (or listen for checkout.card_ready). CARD_READY: confirm may proceed. EXPIRED: the window lapsed — open a fresh one.
cardThe captured card (CARD_READY only).
Open the card window
Opens the Stripe card surface for the session — the ONLY place a card is ever entered (your systems never touch card data). The tenant's active Stripe Connect account is the card rail; there is no provider selection. Refused with CARD_PROVIDER_NOT_CONNECTED when Stripe isn't connected.
uiMode: 'embedded' (default) returns a clientSecret — mount Stripe's embedded Checkout on your page with Stripe.js (loadStripe(publishableKey, { stripeAccount })). uiMode: 'hosted' returns a redirectUrl to checkout.stripe.com; returnUrl is then REQUIRED and its origin must be on your embeddableOrigins allowlist. The flow is identical either way: the card lands asynchronously — poll card-status (or listen for checkout.card_ready), then confirm.
A zero first period still opens the window SAVE-ONLY: the card is stored for the first real charge and nothing is authorized ("no charge today").
Required Scope
checkout
path Parameters
idThe opaque session id (64 hex chars).
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Open the card window › Request Body
uiModeHow the buyer meets Stripe's card form: 'embedded' (default) returns a client secret you mount on your page with Stripe.js; 'hosted' returns a redirect URL to checkout.stripe.com.
returnUrlWhere Stripe sends the buyer back after a HOSTED window. REQUIRED for hosted (its origin must be on your embeddableOrigins allowlist); refused for embedded, which completes in-page.
Open the card window › Responses
modeSTRIPE_EMBEDDED → mount with clientSecret; REDIRECT → navigate the buyer to redirectUrl.
handoffTokenThe card handoff token. It outlives the session: carry it into a fresh session’s confirm after an expiry and the buyer never re-enters card details.
expiresAtWhen the provider window expires (ISO 8601).
clientSecretStripe embedded Checkout client secret (STRIPE_EMBEDDED only).
publishableKeyPublishable key for Stripe.js (STRIPE_EMBEDDED only).
stripeAccountIdThe connected Stripe account id for Stripe.js (STRIPE_EMBEDDED only).
redirectUrlThe hosted window URL to send the buyer to (REDIRECT only).
Open a card-update window for a live subscription
The partner-UI path for updating the card on an EXISTING subscription — same uiMode contract as the checkout card window, rendered to the buyer in your page. The window is save-only and binds to the buyer's stored payment profile; when the new card lands (Stripe webhook), it REPLACES the card on file for that subscription. The buyer's emailed manage link remains the compliance floor and keeps working unchanged.
Required Scope
checkout
path Parameters
idThe subscription id (any id in its version chain).
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Open a card-update window for a live subscription › Request Body
uiModeHow the buyer meets Stripe's card form: 'embedded' (default) returns a client secret you mount on your page with Stripe.js; 'hosted' returns a redirect URL to checkout.stripe.com.
returnUrlWhere Stripe sends the buyer back after a HOSTED window. REQUIRED for hosted (its origin must be on your embeddableOrigins allowlist); refused for embedded, which completes in-page.
Open a card-update window for a live subscription › Responses
modeSTRIPE_EMBEDDED → mount with clientSecret; REDIRECT → navigate the buyer to redirectUrl.
handoffTokenThe card handoff token. It outlives the session: carry it into a fresh session’s confirm after an expiry and the buyer never re-enters card details.
expiresAtWhen the provider window expires (ISO 8601).
clientSecretStripe embedded Checkout client secret (STRIPE_EMBEDDED only).
publishableKeyPublishable key for Stripe.js (STRIPE_EMBEDDED only).
stripeAccountIdThe connected Stripe account id for Stripe.js (STRIPE_EMBEDDED only).
redirectUrlThe hosted window URL to send the buyer to (REDIRECT only).