LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • API Reference
Subscriptions
Products & Pricing
Billing documents
WebhooksMCP (AI agents)
Customer Portal
    Portal SSO
    Embedded Checkout
      OverviewPricing cards & snippetLifecycle eventsBind to a customerGated plansFulfillment & errors
Embedded Checkout

Fulfillment & errors

Fulfillment & reconciliation

Fulfill and reconcile through LedgerBee webhooks, not the client redirect or the checkoutConfirmed browser event. The bound subscription fires subscription.started / subscription.assigned (and every later subscription webhook) carrying partnerReferenceId — the clientReferenceId you passed at provision — so you can match our subscription to your order. Subscribe to those topics and key your fulfillment on partnerReferenceId.

Recover a missed webhook. If a delivery is lost (your endpoint was down, you never subscribed yet, a backfill), resolve our subscription from your own order id over the public API: GET /v1/subscriptions?partnerReferenceId=<your clientReferenceId>. The subscription read responses now carry partnerReferenceId, and the list accepts it as a filter — so the join key is reachable without replaying the webhook. (Requires the subscriptions-read scope; see the list-subscriptions operation.)

The ledgerbee:embed:checkoutConfirmed browser event AND the breakout returnUrl redirect are UX-only — use them to remove the iframe, show a thank-you, or hand the buyer back to your app, never as a fulfillment trigger. A browser that closes mid-redirect drops the event, and the anonymous path redirects to returnUrl with ?checkout=pending once a magic-link has only been sent — the subscription does not exist until the buyer clicks it. returnUrl therefore carries ?subscriptionId= ONLY on CONFIRMED; ?checkout=pending is "inbox sent", not a completion. The webhook is the source of truth.

Errors & failure states

SituationWhat you observeWhat to do
Plan not public yetthe /embed/<vanity> iframe shows a "not found" stateoperator publishes the plan + allows it via a portal routing rule
Your origin not allowlistedthe browser refuses to frame the embed (CSP frame-ancestors); a console error, and no events fireoperator adds your site's origin to the partner-origin allowlist (Settings → Portal → Embedding)
Provision: missing scopeHTTP 403, body { "code": "INSUFFICIENT_PERMISSIONS" }the API key needs portal-provision (+ portal-sso-mint for the token)
Provision: unknown customerIdHTTP 404, body { "code": "CUSTOMER_NOT_FOUND" }the customerId must reference an existing customer in your tenant; this call never creates one from an id (send a customer object to create)
Bind ref expired / reused / no providerthe embed proceeds with the anonymous (magic-link) checkout; no hard errorregister a fetchBindToken provider that mints a fresh ref per call; if a buyer dwells past the ~60s TTL, re-starting checkout mints a new one
Bind ref for the wrong tenant, or for a different customer than a signed-in buyerPOST /api/checkout/session returns 400 PORTAL_CHECKOUT_SESSION_INVALID (reason: TENANT_OR_ITEM_MISMATCH); the embed bounces back to the cards and fires ledgerbee:embed:checkoutErrora security reject; don't hand a checkout a foreign-tenant ref, or an authenticated buyer a foreign-customer ref
Checkout session expired (30-min TTL) or already consumedconfirm / resume returns 410 PORTAL_CHECKOUT_SESSION_INVALID (reason: EXPIRED_OR_NOT_FOUND)re-start checkout for a fresh session — distinct from a forged id (400)
Duplicate confirm (double-click, network retry)409 PORTAL_CHECKOUT_IN_PROGRESSthe first submit is being processed; don't resubmit — exactly one subscription is created
Buyer abandonsnothing is persistedserver-side checkout sessions expire on their own (30 min)

Operator prerequisites (configured in LedgerBee → Settings → Portal → Embedding)

  • The plan has a public vanity URL and is published + allowed by a routing rule.
  • Your site's origin(s) are on the tenant's partner-origin allowlist. This drives both CORS and the frame-ancestors policy; the embed refuses to frame on an un-allowlisted origin.
  • "Allow in-frame checkout" is on if you want target=inline or any item-pinned checkout.
Last modified on June 15, 2026
Gated plans
On this page
  • Fulfillment & reconciliation
  • Errors & failure states
  • Operator prerequisites (configured in LedgerBee → Settings → Portal → Embedding)