Checkout
Confirm the checkout
Credential: a company's API key or OAuth token.
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
Credential: a company's API key or OAuth token.
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), a discount code that takes the first period below what a card can be charged, or a subscription that bills in arrears (its first invoice is charged to the saved card on firstInvoiceDate): adapt your copy ("no charge today"); the card window still opens save-only. Only a first invoice a discount code takes below what a card can be charged is paid by bank transfer; in arrears the first invoice is charged to the saved card on firstInvoiceDate.
Pass discountCode to price a code the buyer typed: quote.discount carries the code, the discount's name and type (percentage, fixed or per_unit), what it takes off the first period before VAT (firstPeriodAmountOff; quote.plan.items keep the regular price, quote.pricing is net of it), the day the regular price starts (endsAt, null when it never ends) and firstPeriodChargedToCard. A code that is unknown, used up or not for this plan is refused with its DISCOUNT_* code.
Pass billingDirection (advance or arrears) to choose when the buyer pays; absent, the company's default applies. The response states the direction and firstInvoiceDate: with advance the first period is invoiced at confirm, with arrears nothing is invoiced at confirm and the first invoice comes when the first period ends. quote.pricing is the first period either way.
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.
Add-ons the buyer takes with the plan, each named by the catalogue item’s addons[].planId, with optional quantities. An add-on checkout sells bills at the plan’s period, on the same invoice as the plan. The same planId twice is refused with VALIDATION_FAILED. Absent or empty → the plan alone.
discountCodeA discount code the buyer typed. Priced onto the first period (quote.discount states what it takes off, until when, and whether the first period is charged to the card) and attached to the subscription for its periods. An unknown, used-up or inapplicable code is refused with its DISCOUNT_* code; each request naming a code spends one use of the API key’s code-attempt budget (429 DISCOUNT_CHECK_RATE_LIMITED).
billingDirectionWhether the subscription bills each period in advance or in arrears. advance: the first period is invoiced on firstInvoiceDate, which is at confirm for a start date of today. arrears: nothing is invoiced at confirm, and the first invoice comes when the first period ends (firstInvoiceDate). Absent → the company’s default billing direction. The session pins the direction; confirm creates the subscription in it.
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, the quantity rows of every add-on the card offers (addonQuantityRows, each naming its add-on by planId; addonSubscriptionId carries the same value), terms bodies, the resolved start date, allowed payment methods and cardPaymentsEnabled. With a discountCode, discount states what the code takes off the first period before VAT (firstPeriodAmountOff), the day the regular price starts (endsAt, null when it never ends) and whether the discounted first period is charged to the card (firstPeriodChargedToCard); plan.items keep the regular price and pricing is net of the discount. Null without a code.
firstPeriodChargeableFalse when the first period nets to zero (free trial / 100% discount), when a discount code takes it below what a card can be charged while the regular price is not, or when the subscription bills in arrears (its first invoice is charged to the saved card on firstInvoiceDate). The card window still opens save-only — the card is stored for the first real charge and nothing is authorized ("no charge today"). A first invoice a discount code takes below the card minimum is then paid by bank transfer and the card is charged from the first full-price period; in arrears the first invoice is charged to the saved card on firstInvoiceDate.
billingDirectionThe direction the subscription would bill in: the request’s billingDirection, else the company’s default.
firstInvoiceDateThe day the first invoice is issued (YYYY-MM-DD). advance: today for a start date of today, or the start date less the company’s send offset for a later one. arrears: the end of the first period. The first invoice bills the first period quote.pricing states.
Integration readiness
Credential: a company's API key or OAuth token.
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
Credential: a company's API key or OAuth token.
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. Pass discountCode to pin a code the quote priced; pinned.discountCode echoes it, and the card window and confirm judge it again before anything is created. With existingCustomerId the code is judged for that customer, so a code made for them is accepted; confirm judges it again once the buyer is proven to be that customer, and otherwise refuses with nothing created and the card hold released. Pass billingDirection to pin when the buyer pays; pinned.billingDirection and pinned.firstInvoiceDate echo it (the company's default when absent), and confirm creates the subscription in that direction.
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.
Add-ons the buyer takes with the plan, each named by the catalogue item’s addons[].planId, with optional quantities. An add-on checkout sells bills at the plan’s period, on the same invoice as the plan. The same planId twice is refused with VALIDATION_FAILED. Absent or empty → the plan alone. The session pins the selection; confirm sells exactly what was pinned.
discountCodeA discount code the buyer typed. Priced onto the first period (quote.discount states what it takes off, until when, and whether the first period is charged to the card) and attached to the subscription for its periods. An unknown, used-up or inapplicable code is refused with its DISCOUNT_* code; each request naming a code spends one use of the API key’s code-attempt budget (429 DISCOUNT_CHECK_RATE_LIMITED).
billingDirectionWhether the subscription bills each period in advance or in arrears. advance: the first period is invoiced on firstInvoiceDate, which is at confirm for a start date of today. arrears: nothing is invoiced at confirm, and the first invoice comes when the first period ends (firstInvoiceDate). Absent → the company’s default billing direction. The session pins the direction; confirm creates the subscription in it.
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, when a discount code takes it below what a card can be charged, or when the session bills in arrears (save-only card window).
Rehydrate a session
Credential: a company's API key or OAuth token.
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, when a discount code takes it below what a card can be charged, or when the session bills in arrears.
cardWindowOpenedWhether a card window has been opened on this session.
Release the card hold (edit-after-card)
Credential: a company's API key or OAuth token.
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
Credential: a company's API key or OAuth token.
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
Credential: a company's API key or OAuth token.
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 window for a live subscription
Credential: a company's API key or OAuth token.
The partner-UI path for adding or 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 card lands (Stripe webhook), it becomes the card on file for that subscription and automatic charging turns on. Read the subscription until paymentMethodId is set. The buyer's emailed manage link remains the compliance floor and keeps working unchanged.
Adding a first card, or replacing a card from another provider, requires the subscription to allow Stripe: set allowedPaymentProviders: ["STRIPE"] on POST /v1/subscriptions or PATCH /v1/subscriptions/{id}. Without it the call answers 400 SUBSCRIPTION_MANAGE_PROVIDER_NOT_ALLOWED.
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 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 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).