Skip to main content
This example gives a chat customer 10 free messages each month and offers 100 more messages for $10. One message costs one credit. Work in Sandbox with a ledger_test_ key, and keep that key on your server. The cURL path works with the public API today. The Python and TypeScript examples use unpublished Python SDK source and unpublished TypeScript SDK source from a repository checkout. Ledger does not assign the free plan when a customer is created. Your application explicitly attaches it. A credit pack adds to the customer’s balance without replacing the free plan. Run the steps in order; later SDK snippets reuse the ledger client initialized in step 1.

1. Create the three catalog items

Create a feature for metering, a free monthly plan, and a one-time credit_pack. Choose IDs unique to your workspace. These are independent creation calls; none replaces the rest of the catalog.
The plan’s zero price means it needs no Stripe Checkout. The pack costs 1000 cents, or $10. A repeated create with the same active ID returns 409 already_exists; use the individual update endpoint to change an existing item.

2. Create a customer and attach the free plan

Use the stable ID from your application’s account system. Customer creation is safe to repeat, but does not grant credits. The separate attach call activates the free plan and grants 10 credits immediately.
The free-plan attach response contains subscriptionId and status: "active", with no Checkout URL. Attaching a different free plan to this customer returns a conflict; a paid plan can instead start Checkout while the free plan remains active until verified payment. The monthly free allowance renews when Ledger next reads that customer’s balance after a period ends.

3. Check access and record completed work

Check before an action. A check reports the current balance but does not reserve credits. After the action succeeds, track it with one stable request ID for that action. Reuse that ID only to retry the same action after a lost response.
With a fresh customer, check returns allowed: true, availableCredits: 10, and creditCost: 1. After one track, the balance is 9. Repeating that track with the same customer, feature, and request ID returns idempotent: true and still leaves 9 credits. A different feature with the same request ID returns 409 request_id_conflict. For a sandbox exhaustion test, record nine more simulated completed actions, each with a different request ID. Use a fresh customer ID when repeating this exercise; a reused request ID remains idempotent.
The next check returns HTTP 200 with allowed: false, availableCredits: 0, and creditCost: 1. A new track attempt returns 409 insufficient_credits; present the top-up option instead of treating that as a successful charge. A check can be allowed and a later track can still return 409 if another request spent the credits in between. Ledger cannot make an external action and its usage record one atomic transaction.

4. Sell a credit pack through Stripe test Checkout

In Developer → Stripe, connect a Stripe test key. In Developer → Checkout, save valid success and cancel URLs. The billing walkthrough covers the test payment and webhook checks. Call attach with addOnId, then send the returned URL to this customer.
The response includes addOnPurchaseId and checkoutUrl. Open the hosted Stripe page and complete the test-mode $10 payment. The browser’s success redirect is not payment proof: Ledger adds 100 credits only after its signed paid Checkout webhook is processed. A pending purchase grants no credits. Retrying the same attach while its Stripe Checkout session is open returns the existing link. If Stripe has expired that session, Ledger closes the stale attempt and creates a new purchase and link on retry.

5. Show the updated balance

Read the customer from your backend after payment confirmation. If you used all ten free credits in the sandbox test, the balance becomes 100. If you tracked only message_001, it becomes 109. Ledger spends monthly plan credits before purchased pack credits.
The dashboard customer page also shows the active free plan, paid pack, recent usage, and credits by source. To show this in your own application, follow Build a customer billing page. See Credits and usage for concurrency and error handling.