Portal SSO
Get a tenant's SSO signing keys (JWKS)
The jwks_uri a partner registers at their OpenID Provider so it can verify our private_key_jwt client assertions. Returns an RFC 7517 JWK Set of the tenant's current signing key plus — during a rotation grace window — the immediately-previous key, each keyed by kid. Public key material only; no authentication required. Never returns a 404: a slug with no configured signing key — unknown, or a real tenant that has not set up portal SSO — returns a single well-formed decoy key rather than an empty set, so a 200 does not confirm the tenant exists. Only a slug that is not a valid DNS label returns the empty set. Cached for 5 minutes; OPs re-fetch on an unknown kid.
path Parameters
slugThe tenant's public portal slug.
Get a tenant's SSO signing keys (JWKS) › Responses
The published public JWK(s). Public key material only — never any private component.
Mint a checkout-vouch token (partner-vouched anonymous checkout)
Vouches for a buyer you have already authenticated, so the embedded checkout can write their customer record at the moment they confirm — instead of you provisioning one up front.
Compared with the bound flow (portal-sso/provision + mintCheckoutBindToken):
- The Details step stays editable, so the buyer corrects anything you pre-filled imprecisely. Only
emailis locked, because it becomes their portal login. - Nothing is written for an abandoned checkout. The customer, portal user and organization are written at confirm.
- The buyer never leaves your page — no magic-link email, unlike a plain anonymous checkout.
Returning buyers
Omit customerId and the confirm creates a customer, emitting customer.created.
Pass customerId and it updates that customer instead, so a buyer who resubscribes keeps one record rather than accumulating a duplicate per purchase. The customer number is preserved; the buyer's Details submission overwrites the billing fields the form covers, and no customer.created fires.
Reuse is allowed only for a customer the vouched email already holds an active portal membership on, or one no portal user holds yet. Anything else is refused: 403 PORTAL_CHECKOUT_VOUCH_CUSTOMER_NOT_THEIRS when it belongs to someone else — omit customerId and let a colleague get their own record — and 403 PORTAL_CHECKOUT_VOUCH_CUSTOMER_ACCESS_REVOKED when the buyer's own portal access has been switched off, which the tenant restores from the gated app.
Pass clientReferenceId either way: it reaches the created subscription, every subscription webhook, and the customer.created event, and it is the only field that maps a new customer back to your order.
Required Scope
checkout— the documented forward scope for every partner checkout capability, ORportal-provision— accepted for compatibility: existing integrations minted against the provisioning scope keep working unchanged.
Tenant preconditions
- Partner-vouched checkout must be enabled on the tenant's portal settings, or the request returns
403 PORTAL_CHECKOUT_VOUCH_DISABLED. The scope proves who is calling; the switch proves the tenant turned the rail on. - Some self-service rail must be reachable by the buyer afterwards — a served portal a new buyer can log into, or the manage-link shell. A tenant with neither returns
403 PORTAL_CHECKOUT_VOUCH_TENANT_PORTAL_UNREACHABLE— a vouched confirm mints no session, so the buyer needs a way to manage or cancel the subscription.
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Mint a checkout-vouch token (partner-vouched anonymous checkout) › Request Body
emailThe buyer you are vouching for. This becomes their portal login identity, so the embedded checkout pre-fills it and LOCKS it — a buyer who is not this person uses the checkout's own escape rather than typing over it, and a confirm carrying a different address is refused. Distinct from contactEmail below, which is only where the paperwork goes.
contactEmailWhere invoices and receipts should go, when that differs from the login identity above. Written to the customer's default contact. Omit and billing follows email.
customerIdA customer you already hold the LedgerBee id for - typically one whose earlier subscription ended and who is now buying again. The purchase lands on that record instead of a second one, so a returning buyer does not accumulate duplicates. The customer must exist, be active, and belong to the API key's tenant; otherwise the mint returns 404 CUSTOMER_NOT_FOUND and nothing is minted.
This does not make the checkout read-only: the Details step still shows only what you prefilled, never the stored record, and whatever the buyer submits is written over the record at confirm. Omit for a first-time buyer - creating the customer at confirm is the default.
Optional starting values for the checkout Details step. Every field stays EDITABLE — this is a convenience for the buyer, never an assertion of fact, and whatever they submit is what we persist. Anything you omit renders empty.
allowIdentityChangeWhether the buyer may reject the vouched identity and continue as an anonymous buyer (the checkout shows a "Not you?" escape). It does NOT let them type a different address - the vouch is dropped and the checkout restarts unvouched, because we cannot vouch for someone we were not told about. Defaults to true: a buyer who genuinely is not the person you named has no other way out, and stranding them is worse than showing a link. Set false when your authentication makes a wrong identity impossible.
clientReferenceIdYour own opaque reference for this order (Stripe client_reference_id parity). It rides onto the created subscription (as partnerReferenceId), into every later subscription webhook, AND onto the customer.created event — which is the only field that maps a brand-new customer back to the cart that created it. Pass it. Server-to-server only; it never reaches a client/iframe surface. Max 200 chars, [A-Za-z0-9_-] only.
Mint a checkout-vouch token (partner-vouched anonymous checkout) › Responses
checkoutVouchTokenOpaque, single-use, 60-second vouch reference. Return it from your page's fetchBindToken provider at checkout-start; the embedded checkout redeems it server-side. Mint it just-in-time — the TTL is deliberately shorter than a page's lifetime, and the provider is re-invoked per checkout.
expiresAtISO 8601 expiry of the reference above.
Mint a customer-portal SSO handoff reference
Back-channel single sign-on into the LedgerBee customer portal. A partner's backend calls this to mint an opaque, single-use, 60-second reference for a customer-portal user; the user's browser then redeems that reference at the portal-API host (
Required Scope
portal-sso-mint
Required License
- Customer Portal — the tenant must also hold this module license, or the request returns
403 LICENSE.REQUIRED.
Mint a customer-portal SSO handoff reference › Request Body
emailEmail of the partner-provisioned user. Links a first-time SSO login to an existing invited customer-portal user.
subThe partner's stable, unique identifier for the user. Keyed together with the per-tenant handoff issuer to resolve the portal user across logins.
At least one customer membership to grant this user — required. The customer's portal organization (an opaque internal container) and the membership are created automatically server-side at redeem, so a prior provision call is NOT needed; you only ever reference customers, never organizations. Validated at mint: a customerId that isn't a customer of the API key's tenant, or an unknown role, is rejected with a 400 (rather than failing the user's browser redirect). A handoff with no membership can't establish a portal session, so an empty/omitted array is rejected here rather than minting a ref that would fail at redeem.
nameDisplay name of the user; used to populate the customer-portal user's name on first login.
Mint a customer-portal SSO handoff reference › Responses
refOpaque, single-use, short-TTL reference. Carries NO identity — the identity lives only in server-side storage keyed by this reference until it is redeemed once at the customer portal.
expiresAtISO-8601 timestamp after which the reference can no longer be redeemed (60 seconds after minting).
Provision a customer + grant portal access (partner JIT provisioning)
Ensures a customer (referenced by customerId, or upserted idempotently by customer.customerNumber within the API key's tenant), bootstraps its portal organization, and grants a credential-less portal user an active membership — so a net-new end-customer can sign in via SSO/handoff. No invitation email is sent. Set mintHandoff=true (and carry the portal-sso-mint scope) to also receive a one-time handoff reference in the response for a single round-trip 'Go to portal'. Set mintCheckoutBindToken=true (also requires the portal-sso-mint scope) to receive a one-time checkout-bind reference the partner embeds so the embedded checkout subscribes directly against this customer.
Required Scope
portal-provision
Required License
- Customer Portal — the tenant must also hold this module license, or the request returns
403 LICENSE.REQUIRED.
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Provision a customer + grant portal access (partner JIT provisioning) › Request Body
emailEmail of the portal user to grant access to. A credential-less portal user is created if none exists.
customerIdThe customer's LedgerBee id. The customer must already exist in the API key's tenant; this endpoint never creates one from an id (a 404 CUSTOMER_NOT_FOUND is returned if it doesn't). Supply either customerId or a customer payload.
A customer to upsert, identified idempotently by its customerNumber: an existing customer of the API key's tenant with that number is reused, otherwise a new one is created with this payload. Same shape as the gated POST /customers request (so vatZone and countryCode are required here). If customer.email is omitted, the portal-user email below is used as the customer's contact email (and its auto-created default contact). Supply either this or customerId.
roleMembership role to grant. Defaults to USER; the org's first member is force-assigned OWNER regardless.
userNameDisplay name of the portal user (set on first creation).
organizationDisplayNameDisplay name for the customer organization (first-invite bootstrap).
mintHandoffWhen true, also mint a back-channel handoff reference in the same call (one round-trip "Go to portal"). Requires the API key to ALSO carry the portal-sso-mint scope, and the tenant to have back-channel handoff enabled.
subThe partner's stable, unique user identifier — required when mintHandoff is true; keys the handoff identity (with the per-tenant handoff issuer).
mintCheckoutBindTokenWhen true, also mint a one-time CHECKOUT-BIND reference in the same call — an opaque token the partner's fetchBindToken provider returns to the embed at checkout-start so the embedded checkout subscribes directly against THIS customer instead of staging an anonymous magic-link signup. Requires the API key to ALSO carry the portal-sso-mint scope. Independent of mintHandoff (login): a checkout-bind ref can never mint a login session.
clientReferenceIdYour own opaque reference for this order (Stripe client_reference_id parity), e.g. your internal order or cart id. It rides the minted checkout-bind ref onto the resulting subscription (as partnerReferenceId) and into every later subscription webhook, so you can reconcile our subscription to your record. Server-to-server only — it never reaches a client/iframe surface. Max 200 chars, [A-Za-z0-9_-] only.
Provision a customer + grant portal access (partner JIT provisioning) › Responses
customerIdResolved customer id.
customerCreatedWhether a new customer was created (false = an existing one was reused).
portalUserIdResolved portal user id.
membershipIdThe active organization membership id.
roleThe granted membership role.
handoffRefOpaque, single-use, 60s handoff reference — present only when mintHandoff was requested. Null otherwise.
handoffExpiresAtISO-8601 expiry of the handoff reference, when minted. Null otherwise.
checkoutBindTokenOpaque, single-use, 60s checkout-bind reference — present only when mintCheckoutBindToken was requested. Null otherwise. The partner's fetchBindToken provider returns this to the embed at checkout-start; the embedded checkout redeems it to subscribe directly against this customer.
checkoutBindExpiresAtISO-8601 expiry of the checkout-bind reference, when minted. Null otherwise.
Revoke (force-refresh) a user's customer-portal sessions
Immediately invalidates every active portal session of the partner-provisioned user (identified by the same sub used at mint), forcing re-authentication on their next request. Use for proactive offboarding — it tightens the residual window from the session TTL to immediate. Deprovisioning at the OP is what then prevents re-login.
Required Scope
portal-sso-mint
Required License
- Customer Portal — the tenant must also hold this module license, or the request returns
403 LICENSE.REQUIRED.
Revoke (force-refresh) a user's customer-portal sessions › Request Body
subThe partner's stable subject id for the user — the same sub used when minting their handoff. Every active portal session of that user is force-refreshed.
Revoke (force-refresh) a user's customer-portal sessions › Responses
revokedNumber of memberships whose sessions were force-refreshed (0 when the user has no active sessions).