# Billing API

> Read the credit ledger and create Lemon Squeezy checkout links for credit packs.

Source: https://akaphone-docs.pages.dev/api/billing/

See the [Billing guide](https://akaphone-docs.pages.dev/guides/billing/) for packs, plans, receipts and refunds.

## List ledger entries

`GET /v1/billing/ledger`

Every credit added to or taken from your balance, newest first.

| Query | Type | Default | Description |
|---|---|---|---|
| `limit` | integer | 50 | 1–500. |

```bash title="curl"
curl "https://akaphone.abhgnx-417.workers.dev/v1/billing/ledger?limit=20" \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"
```
```typescript title="TypeScript"
const { entries } = await fetch("https://akaphone.abhgnx-417.workers.dev/v1/billing/ledger?limit=20", {
  headers: { Authorization: `Bearer ${process.env.AKAPHONE_API_KEY}` },
}).then((r) => r.json());
```
```python title="Python"
import os, requests

entries = requests.get(
    "https://akaphone.abhgnx-417.workers.dev/v1/billing/ledger",
    params={"limit": 20},
    headers={"Authorization": f"Bearer {os.environ['AKAPHONE_API_KEY']}"},
).json()["entries"]
```

**Response** `200 OK`

```json
{
  "entries": [
    { "id": "led_01m5vqsywgmvjvevzqnbkz", "delta": -10, "kind": "usage", "ref": "dev_s6zr7mr4f2f1:w1", "created_at": "2026-10-26T09:24:09.000Z" },
    { "id": "led_01m5vqsywgmzjsatq42611", "delta": 1375, "kind": "purchase", "ref": "4821733", "created_at": "2026-10-26T09:02:51.000Z" },
    { "id": "led_01m5vqsywgadfgngs4y1q2", "delta": 60, "kind": "grant", "ref": "signup:usr_01m5vqsywg3mxj7dyxv9q9", "created_at": "2026-10-20T15:40:12.000Z" }
  ]
}
```

| Field | Description |
|---|---|
| `id` | `led_…` |
| `delta` | Credits added (positive) or removed (negative). |
| `kind` | `grant`, `purchase`, `usage`, `refund` or `adjust`. |
| `ref` | What it refers to: `signup:<user id>` for the signup grant, the Lemon Squeezy order id for purchases, `<device id>:w<n>` for the *n*th 10-minute usage window of a device, `<user id>:YYYY-MM` for usage combined by month after 90 days, `<order id>:r<amount>` for refunds. |
| `created_at` | When it was recorded. |

## Create a checkout

`POST /v1/billing/checkout`

Creates a Lemon Squeezy hosted checkout for a credit pack and returns its URL. Open the URL in a browser to pay. Credits are added when Lemon Squeezy confirms the payment, and the buyer is sent back to `https://akaphone.abhgnx-417.workers.dev/billing?checkout=success`.

| Body field | Type | Description |
|---|---|---|
| `pack` | string | `"250"` ($10), `"1375"` ($50), `"6000"` ($200) or `"team"` ($199, 6,000 credits and the Team plan for 30 days). |

```bash title="curl"
curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/billing/checkout \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pack": "250"}'
```
```typescript title="TypeScript"
const { url } = await fetch("https://akaphone.abhgnx-417.workers.dev/v1/billing/checkout", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.AKAPHONE_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ pack: "250" }),
}).then((r) => r.json());
```
```python title="Python"
url = requests.post(
    "https://akaphone.abhgnx-417.workers.dev/v1/billing/checkout",
    headers={"Authorization": f"Bearer {os.environ['AKAPHONE_API_KEY']}"},
    json={"pack": "250"},
).json()["url"]
```

**Response** `200 OK`

```json
{ "url": "https://akaphone.lemonsqueezy.com/checkout/custom/4f1c0e7a-…" }
```

| Error | When |
|---|---|
| `400 invalid_request` | `pack` is not one of the four ids. Note that pack ids are strings: `"250"`, not `250`. |
| `500 internal` | The checkout could not be created at Lemon Squeezy. Retry later. |

To check that a purchase went through, look for a `purchase` entry in the ledger or a higher `credits.balance` in [`GET /v1/me`](https://akaphone-docs.pages.dev/api/me/).
