LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • Configuration
  • API Reference
Subscriptions
Payment flowCard paymentsProducts & PricingProduct Entitlements
Billing documents
Webhooks
Customer Portal
    Portal SSO
    Embedded Checkout
      OverviewPricing cards & snippetLifecycle eventsBind to a customerGated plansFulfillment & errors
Embedded Checkout

Embedded Checkout

Embed your pricing cards and checkout directly onto your own site. The cards render in a sandboxed iframe served from your portal origin (https://<slug>.portal.ledgerbee.com). Checkout runs in-frame or breaks out to a new tab. Card capture stays on the payment provider's hosted window, so your page never touches card data.

Placeholders: <slug> is your tenant subdomain. <vanity> is a plan's public vanity slug — the same slug as its /p/<vanity> page (e.g. pro-plan), never a plan UUID. <planItemId> is a specific item's id, required for the item-pinned and forced-checkout surfaces (see Three surfaces). All three appear in the operator UI at Settings → Portal → Embedding, which generates a ready-made snippet with the ids filled in. Copy that snippet rather than hand-building the URL.

This guide is split across pages: Pricing cards & snippet, Lifecycle events, Bind to a customer, Gated (non-public) plans, and Fulfillment & errors.

When a customer account & subscription are created

Embedding or displaying pricing never creates a customer or subscription. Both the public cards and the gated cards resolved for a specific buyer are read-only: the catalogue reads and POST /v1/portal/plans/resolve run the visibility rules and (for a gated buyer) mint a short-lived, read-only display token, but write nothing to your ledger. Resolving with a customerId reads an existing customer; resolving with countryCode / customerType hints reads no customer at all. A visitor who is shown cards and leaves persists nothing.

Records are created only at checkout, and the timing depends on the flow:

FlowWhen the customer + subscription are created
Anonymous checkout (no binding)When the buyer completes checkout and clicks the magic-link verification email. Submitting the form only stages a self-expiring pending sign-up and sends the email; the subscription does not exist until the link is clicked.
Bound checkout (you provision)The customer is created (or reused) by your POST /v1/portal-sso/provision call; the subscription is created when the buyer confirms the bound checkout. No magic-link verification.

A buyer who abandons before these points leaves no customer or subscription — only the server-side checkout session, which expires on its own (30 min). Any card captured before abandonment lives on the payment provider, not your ledger, and is cleaned up automatically.

Skipping the magic-link verification

The magic-link step exists only on the anonymous flow; it is how an anonymous buyer's record is deferred until they prove their email. To skip it, use the bound flow: provision the customer and bind the checkout, and the buyer subscribes in one step with no email round-trip.

Binding is decided when the checkout session is created — at the buy click or forced-checkout mount, via your fetchBindToken provider — not at confirm. The provision call therefore runs before the bound checkout, and there is no path to let a buyer confirm anonymously and then convert the in-progress checkout to a bound one. For a buyer you already know (a signed-in user of your platform), provision at the buy click and run the bound checkout directly; the signup-first funnel (target=callback) catches the buy click, provisions on the buyer's behalf, then drops them into the bound checkout.

Skipping verification is a trade-off: the customer record is then created at the buy click rather than only on completion, so a buyer who abandons a bound checkout leaves a customer with no subscription. provision is idempotent, so you control when — and whether — to create it.

Three surfaces (same embed.js)

These three surfaces address a plan by its public <vanity> slug, so they require a public plan (published + a vanity URL + an allow-all routing rule). To embed a non-public plan for a buyer you've already identified, use the by-id surfaces in Gated (non-public) plans instead.

Checkout always targets one item. There is no standalone vanity-only checkout — that is the pricing card. The three surfaces are:

SurfaceURLShowsUse when
Pricing cardhttps://<slug>.portal.ledgerbee.com/embed/<vanity>the plan's card(s)The buyer compares options; the buy CTA starts the chosen item's checkout.
Item checkout — keeps cardshttps://<slug>.portal.ledgerbee.com/embed/<vanity>?item=<planItemId>that item's checkout, with the cards loaded behind itYou drop the buyer straight on one item but still let them go Back to the full card set. Always in-frame.
Forced single-item checkouthttps://<slug>.portal.ledgerbee.com/embed/checkout/<vanity>/<planItemId>that item's checkout onlyYou want one item, no cards and no Back. Always in-frame.

All three carry the data-ledgerbee-pricing attribute so embed.js manages them (resize plus the on-demand bind handshake). Every surface requests a fresh bind ref at checkout-start, not on load — see the token lifecycle.

The first path segment is the plan's vanity slug, not a UUID. <planItemId> is a server-side id. Both are filled in by the operator's snippet generator at Settings → Portal → Embedding — pick a snippet type (Pricing cards, Checkout (with cards), or Checkout (item only)) and copy the result rather than constructing the URL by hand.

Quick start (pricing card)

Code
<iframe src="https://<slug>.portal.ledgerbee.com/embed/<vanity>?lang=en&target=inline&theme=system" data-ledgerbee-pricing loading="lazy" style="width:100%;border:0;min-height:520px" title="Pricing"></iframe> <script src="https://<slug>.portal.ledgerbee.com/embed.js" async></script>
  • The companion embed.js is optional but recommended. It auto-resizes the iframe to the content height (no inner scrollbar), re-dispatches lifecycle events as DOM events on your page, and performs the bind-token handshake. It carries no tenant or plan knowledge; one copy serves every embed on the page.
  • The embed renders only once the plan is publicly reachable — the operator must publish it and allow it via a portal routing rule. Otherwise the iframe shows a "not found" state. See Errors.
Last modified on July 30, 2026
Portal SSOPricing cards & snippet
On this page
  • When a customer account & subscription are created
    • Skipping the magic-link verification
  • Three surfaces (same embed.js)
  • Quick start (pricing card)