customerId from your account system; verify that it belongs to the signed-in user before calling Ledger. These examples use Sandbox and a ledger_test_ key. cURL works with the public API today. Python and TypeScript examples use the unpublished Python SDK source and unpublished TypeScript SDK source from a repository checkout.
Show the current plan, available credits, plan history, credit-pack purchases, and eligible renewal controls. Ledger returns billing state; your application renders it and sends the customer to Stripe when a paid Checkout is needed.
1. Load the customer’s billing state
Create the customer if needed, then read their balance and the first page of each history. Creation does not assign a plan or grant credits. Run these calls on your backend and return only the fields your signed-in customer needs.customer.availableCredits is the current balance across active plan and purchased credit grants. customer.currentPlan contains the plan ID, name, status, and period dates, or null. It does not contain a numeric subscription ID or renewal settings. An active free plan can remain currentPlan while a paid plan is pending Checkout; show the pending offer from subscriptions.items. A customer may have pack credits while currentPlan is null.
Render each subscription’s planName, monthlyPriceCents, status, and period dates. Render each purchase’s name, status, and creditAmount. A pending pack has creditAmount: null and grants no credits; a legacy paid row without a linked grant can also have a null amount. A paid row’s amount is what was actually granted, even if the catalog item changes later.
Each history response has items, hasMore, and nextCursor, ordered newest first. To load older rows, repeat that request with its returned numeric nextCursor; stop when hasMore is false. limit defaults to 25 and is capped at 100. Keep subscriptions and purchases on separate cursors.
2. Offer a plan or credit pack
Create plans and credit-pack add-ons before offering them. Send exactly one ofplanId or addOnId with the authenticated customer’s ID. A free plan activates immediately; a paid plan or pack returns a hosted Stripe checkoutUrl.
Start plan Checkout
Sell a credit pack
Use this action when the customer chooses a pack. It creates a separate one-time Checkout.subscriptionId and status: "active", without Checkout. A paid-plan attach responds with subscriptionId and checkoutUrl; a pack attach responds with addOnPurchaseId and checkoutUrl. Retrying the same attach while its Stripe session is open returns the existing link. If Stripe expired that session, Ledger closes the attempt and creates a new one on retry. The browser’s success redirect is not payment proof. Refresh the customer and history after Ledger processes the signed Stripe payment webhook. See Billing and Checkout.
You can attach a paid plan while a free plan is active. For an active paid plan, the supported change is an upgrade to a higher-priced paid plan through Upgrade a paid plan, with an Idempotency-Key. The response may contain a Stripe paymentUrl; the new plan applies only after verified payment. Ledger does not expose a general paid-plan switch or downgrade endpoint.
3. Let eligible customers stop or resume renewal
Use a numeric subscriptionid from subscriptions.items, not the catalog planId. Show Stop renewal or Resume renewal only when the active paid row has cancellation?.manageable === true. If cancellation is null, Stripe could not verify a manageable renewal state; do not present null as proof that auto-renewal is on. scheduledEnd can reflect a Stripe cancellation date even when cancelAtPeriodEnd is false.
cancelAtPeriodEnd: false to resume before the paid period ends. Stopping renewal keeps the current paid period and its remaining plan credits available; purchased packs remain. Stripe state can change between loading the page and pressing the button, so handle a conflict or Stripe error and reload subscriptions. A pending upgrade, pause, schedule, or custom Stripe cancellation date can make renewal unavailable. This endpoint does not cancel an unpaid Checkout link.
Keep the page current
Refresh the customer and both histories after a webhook-confirmed payment or renewal change. A pending Checkout row is useful for progress, but does not include a Checkout URL; keep the URL returned byattach for navigation or retry that same attach to recover an open link. Do not show a completed purchase or new allowance from the browser redirect alone. The sandbox billing test walks through a Stripe test payment and signed webhook verification.
Ledger’s dashboard also shows Stripe invoice and credit-pack payment panels for operators. Those panels are not public customer API endpoints, so build your application page from the customer, subscription, and purchase endpoints above.