> ## Documentation Index
> Fetch the complete documentation index at: https://docs.integrateledger.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Customers

> Create customers, start plan or add-on Checkout, and inspect credit history.

Use your application's stable user ID as Ledger's `customerId`. Your API key selects Sandbox or Live, and the records are separate. `POST /v1/customers` is create-or-get: calling it again returns `created: false` and does not grant credits. `GET /v1/customers?customerId=...` reads the current balance and plan without a feature ID. URL encode the ID when it contains punctuation or spaces. The dashboard **Customers** page also shows subscriptions, add-on purchases, recent usage, and a **Credits by source** breakdown. That breakdown lists current plan and credit-pack grants with their granted, used, and available amounts and expiration dates. It excludes expired and superseded plan grants, so its available amounts add up to the customer balance.

## Start a plan

Create a paid plan and connect the selected environment's Stripe account before calling `POST /v1/billing/attach` with `planId`. Live paid Checkout also requires live activation and Ledger's live-charge gate. Redirect the customer to the returned `checkoutUrl`. The plan is pending until Ledger processes the paid Stripe invoice webhook, which creates the monthly credit allocation.

```bash theme={null}
curl --request POST https://api.integrateledger.com/v1/customers \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"customerId":"user_123"}'

curl --request POST https://api.integrateledger.com/v1/billing/attach \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"customerId":"user_123","planId":"starter"}'
```

## Sell a credit pack

Send `addOnId` instead of `planId` to the same endpoint. Ledger grants pack credits after the paid Checkout webhook. A customer may buy multiple packs over time.

```bash theme={null}
curl --request POST https://api.integrateledger.com/v1/billing/attach \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"customerId":"user_123","addOnId":"extra_credits"}'
```

`attach` is safe to retry while the same Checkout is pending; a different pending purchase or an existing subscription can return a conflict. Check the returned status and use the same customer and item ID for retries.

## Upgrade a paid plan

`POST /v1/billing/upgrade` requires an `Idempotency-Key` header. If `paymentUrl` is returned, send the customer there. Ledger changes the plan after Stripe confirms payment.

```bash theme={null}
curl --request POST https://api.integrateledger.com/v1/billing/upgrade \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: upgrade_user_123_pro" \
  --data '{"customerId":"user_123","planId":"pro"}'
```

See [Billing and Checkout](https://docs.integrateledger.com/billing.md) for Stripe setup and webhook timing.

## Stop or resume a customer's renewal

In the dashboard, open **Customers**, select an active paid customer, and choose **Stop renewal**. Ledger schedules the cancellation at the end of the current paid period in Stripe. The plan and remaining plan credits stay available through that period; purchased credit packs are unaffected. The customer page shows the scheduled end date. Use **Resume renewal** before that date to restore monthly billing. A pending plan upgrade must finish before you change renewal settings. If Stripe has a pending billing change, pause, schedule, or custom cancellation date, manage renewal in Stripe and refresh the customer page; Ledger hides its renewal controls until the subscription can be changed safely.

When Stripe sends the subscription-deleted webhook at the effective end, Ledger closes the plan and expires any remaining credits from that plan's current period. This action does not cancel an unpaid Checkout link; use the separate **Cancel link** action for pending Checkout.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.