# API: balance, licences, withdrawals and invoices

_API · 7 min read · Updated 2026-10-22 · https://lanachat.ai/guides/api-billing/_

LanaChat is paid from one prepaid **balance** in US dollars: **licences** (one per Telegram account, 30 days) and a **commission** on net sales are taken from it. You top it up with crypto deposits. Affiliate earnings can be **withdrawn**. Every billing endpoint needs an **owner** token: in the examples, `$LANACHAT_TOKEN` holds one.

## Endpoints

| Method | Path | Description | Permission | Status |
|---|---|---|---|---|
| GET | `/billing/balance` | The balance, this month's net sales and commission tier | `owner` | Planned |
| GET | `/billing/ledger` | Every movement: deposits, licences, commission, refunds | `owner` | Planned |
| GET | `/billing/ledger.csv` | The same ledger as CSV (`from=`, `to=`) | `owner` | Planned |
| GET | `/billing/deposit-addresses` | One deposit address per chain (with a memo on Solana) | `owner` | Planned |
| GET | `/billing/deposits` | Deposits and their confirmations | `owner` | Planned |
| GET | `/billing/licences` | Licences in stock and assigned, with days left | `owner` | Planned |
| POST | `/billing/licences/buy` | Buy licences: single or a pack of 10, 25 or 50 | `owner` | Planned |
| POST | `/billing/licences/assign` | Give a licence from stock to an account | `owner` | Planned |
| POST | `/billing/licences/unassign` | Take it back to stock with its days left | `owner` | Planned |
| PATCH | `/billing/licences/auto-renew` | Turn auto-renew on or off for an account | `owner` | Planned |
| GET | `/billing/withdrawals` | Affiliate withdrawals and their status | `owner` | Planned |
| POST | `/billing/withdrawals` | Ask for a withdrawal of affiliate earnings (from $500) | `owner` | Planned |
| GET | `/billing/invoices` | Monthly statements (`year=`) | `owner` | Planned |
| GET | `/billing/invoices/{id}.pdf` | One statement as PDF | `owner` | Planned |
| PUT | `/billing/details` | Company name, address and VAT number on statements | `owner` | Planned |

## Prices in one table

| What | Price |
|---|---|
| One licence (one Telegram account, 30 days) | $19 |
| Pack of 10 licences | $170 |
| Pack of 25 licences | $375 |
| Pack of 50 licences | $650 |
| Commission up to $30k of net sales in the month | 12% of AI sales, 4% of manual sales |
| Commission from $30k to $100k | 10% and 3% |
| Commission above $100k | 8% and 2% |

The counter is the net sales of the whole workspace in the calendar month; it resets on the 1st. Telegram Stars count at $0.013 each in net sales.

## The balance object

```json
{
  "balance_cents": 48210,
  "month": "2026-10",
  "net_sales_cents": 1870400,
  "tier": { "up_to_cents": 3000000, "ai_rate_bp": 1200, "manual_rate_bp": 300 },
  "licences": { "in_stock": 4, "assigned": 11, "expiring_7d": 2 },
  "low_balance": false
}
```

| Field | Type | Description |
|---|---|---|
| `balance_cents` | integer | Prepaid balance, in US cents |
| `month` | string | The calendar month counted, `YYYY-MM` |
| `net_sales_cents` | integer | Net sales of the workspace this month |
| `tier` | object | The current tier: its ceiling `up_to_cents`, the AI rate and the manual rate in basis points |
| `licences` | object | Licences `in_stock`, `assigned`, and assigned ones ending within 7 days (`expiring_7d`) |
| `low_balance` | boolean | `true` when the balance does not cover the next 7 days of licences |

Rates are in basis points: `1200` is 12%.

## The ledger entry object

Each line of **Billing › Activity** in the Console.

```json
{
  "id": "led_90211",
  "at": "2026-10-06T14:40:00Z",
  "kind": "licences",
  "label": "Licences · pack of 10",
  "amount_cents": -17000,
  "balance_after_cents": 31210
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The line |
| `at` | string | ISO 8601, UTC |
| `kind` | string | `deposit`, `licences`, `commission`, `refund`, `affiliate` or `other` |
| `label` | string | The text of the line, as in the Console |
| `amount_cents` | integer | Positive when money comes in, negative when it goes out |
| `balance_after_cents` | integer | The balance after this line |

A `commission` line belongs to one sale: the Console shows the fan, the tier, who sent the offer (AI or manual), how the net was counted ("12,500 ★ × $0.013") and the rate.

## The deposit object

```json
{
  "id": "dep_4",
  "at": "2026-10-06T13:52:00Z",
  "chain": "usdt-tron",
  "sent_cents": 100000,
  "fee_cents": 1000,
  "credited_cents": 99000,
  "status": "pending",
  "confirmations": "12/19"
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The deposit |
| `at` | string | When it was seen, ISO 8601 |
| `chain` | string | `usdc-eth`, `usdc-sol`, `usdt-sol`, `usdt-tron` or `usdt-eth` |
| `sent_cents`, `fee_cents`, `credited_cents` | integer | What you sent, the fee, what lands on the balance |
| `status` | string | `pending` then `confirmed` |
| `confirmations` | string | Network confirmations counted, while pending |

USDC on Ethereum is credited 1:1 with no fee. Every other chain has a 1% fee: the deposit is converted to USDC (ERC20) when it arrives. The minimum is $25 per top-up.

## The licence object

```json
{
  "account_id": "acc_1001",
  "creator_id": "cr_lea",
  "days_left": 18,
  "expires_at": "2026-10-24T09:00:00Z",
  "auto_renew": true
}
```

| Field | Type | Description |
|---|---|---|
| `account_id` | string | The Telegram account the licence runs on |
| `creator_id` | string | Her creator |
| `days_left` | integer | Days before it ends, out of 30 |
| `expires_at` | string | When it ends |
| `auto_renew` | boolean | Renew from stock (or buy a single) when it ends |

Licences in stock are counted, not listed one by one: each keeps its own days left (30 for a new one).

## The withdrawal object

```json
{ "id": "wd_118", "status": "pending_review", "amount_cents": 50000, "requested_at": "2026-10-06T15:00:00Z", "tx_hash": null }
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The withdrawal |
| `status` | string | `pending_review`, `sent` (with `tx_hash`) or `rejected` with a reason |
| `amount_cents` | integer | Amount asked, at least 50000 ($500) |
| `requested_at` | string | ISO 8601, UTC |
| `tx_hash` | string or null | The transaction, once sent |

## The invoice object

```json
{
  "id": "LC-2026-09",
  "month": "2026-09",
  "licences_cents": 22800,
  "commission_cents": 930000,
  "total_cents": 952800,
  "status": "paid",
  "pdf_url": "https://api.lanachat.ai/v1/billing/invoices/LC-2026-09.pdf"
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | Statement number, `LC-YYYY-MM` |
| `month` | string | The month it covers |
| `licences_cents`, `commission_cents`, `total_cents` | integer | Licences, commission and total of the month |
| `status` | string | Paid, in progress (the current month) or failed |
| `pdf_url` | string | The PDF of the statement |

## Balance and ledger

### Get the balance

`GET /billing/balance` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/balance \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The answer is the balance object.

### List the ledger

`GET /billing/ledger` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `from`, `to` | string | No | Days `YYYY-MM-DD`, UTC, both included |
| `limit`, `cursor` | number, string | No | [Pagination](https://lanachat.ai/guides/api-overview/) |

```bash
curl "https://api.lanachat.ai/v1/billing/ledger?from=2026-10-01&to=2026-10-06" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

```json
{
  "data": [
    { "id": "led_90211", "at": "2026-10-06T14:40:00Z", "kind": "licences", "label": "Licences · pack of 10", "amount_cents": -17000, "balance_after_cents": 31210 },
    { "id": "led_90207", "at": "2026-10-06T13:20:00Z", "kind": "commission", "label": "Lea · Marco", "amount_cents": -468, "balance_after_cents": 48210 },
    { "id": "led_90150", "at": "2026-10-05T09:02:00Z", "kind": "deposit", "label": "Deposit · USDC on Ethereum", "amount_cents": 300000, "balance_after_cents": 48678 }
  ],
  "next_cursor": "eyJpZCI6ImxlZF85MDE0OSJ9"
}
```

### Export the ledger as CSV

`GET /billing/ledger.csv` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `from`, `to` | string | No | Days `YYYY-MM-DD`, UTC |

```bash
curl "https://api.lanachat.ai/v1/billing/ledger.csv?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $LANACHAT_TOKEN" -o ledger-2026-09.csv
```

The same lines as **Export CSV** in **Billing › Activity**, ready for your accountant.

## Deposits

### Get the deposit addresses

`GET /billing/deposit-addresses` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/deposit-addresses \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The answer gives one address per chain (Ethereum, Solana, Tron) and, for Solana, the memo.

> Warning: On Solana, a deposit sent without the memo cannot be matched to your workspace. Always send both the address and the memo.

You can send USDC on Ethereum (ERC20), USDC on Solana, USDT on Solana, USDT on Tron (TRC20) or USDT on Ethereum (ERC20). The network fee of your transfer is paid from your wallet.

### List deposits

`GET /billing/deposits` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/deposits \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

```json
{
  "data": [
    { "id": "dep_4", "at": "2026-10-06T13:52:00Z", "chain": "usdt-tron", "sent_cents": 100000, "fee_cents": 1000, "credited_cents": 99000, "status": "pending", "confirmations": "12/19" },
    { "id": "dep_3", "at": "2026-10-05T09:02:00Z", "chain": "usdc-eth", "sent_cents": 300000, "fee_cents": 0, "credited_cents": 300000, "status": "confirmed", "confirmations": null }
  ],
  "next_cursor": null
}
```

The balance is credited automatically when a deposit is `confirmed`; it is then also a `deposit` line of the ledger.

## Licences

### List licences

`GET /billing/licences` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/licences \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The answer gives the licences in stock and one licence object per assigned account, with its days left.

### Buy licences

`POST /billing/licences/buy` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `pack` | integer | Yes | `1` (single, $19), `10` ($170), `25` ($375) or `50` ($650) |
| `qty` | integer | Yes | How many of that pack |

```bash
curl -X POST https://api.lanachat.ai/v1/billing/licences/buy \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"pack": 10, "qty": 1}'
```

```json
{ "licences_added": 10, "amount_cents": 17000, "balance_cents": 31210, "ledger_id": "led_90211" }
```

The licences go to your stock at once. A balance too low answers `402` (`insufficient_balance`). Licences in stock never expire, and bought licences are not refundable.

### Assign a licence

`POST /billing/licences/assign` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `account_id` | string | Yes | The Telegram account that gets a licence |

```bash
curl -X POST https://api.lanachat.ai/v1/billing/licences/assign \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "acc_1001"}'
```

The answer is the licence object of the account. The stock licence with the fewest days left is used first: its remaining days, or 30 days if it is new. Auto-renew is on by default.

### Take a licence back

`POST /billing/licences/unassign` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `account_id` | string | Yes | The account that gives its licence back |

```bash
curl -X POST https://api.lanachat.ai/v1/billing/licences/unassign \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "acc_1001"}'
```

The licence goes back to your stock with its days left. The AI stops replying on that account right away. To move a licence to another account, take it back, then assign it: it keeps its days, for free.

### Turn auto-renew on or off

`PATCH /billing/licences/auto-renew` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `account_id` | string | Yes | The account |
| `auto_renew` | boolean | Yes | `true` to renew it when its days run out |

```bash
curl -X PATCH https://api.lanachat.ai/v1/billing/licences/auto-renew \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "acc_1001", "auto_renew": false}'
```

When the days run out, auto-renew takes a licence from your stock first, or buys a single licence ($19) if the stock is empty. It cannot buy without balance: watch `low_balance` or the `billing.low_balance` [webhook](https://lanachat.ai/guides/api-webhooks/).

## Affiliate withdrawals

### List withdrawals

`GET /billing/withdrawals` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/withdrawals \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

A list of withdrawal objects, newest first.

### Ask for a withdrawal

`POST /billing/withdrawals` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `amount_cents` | integer | Yes | At least 50000 ($500), at most what is available to withdraw |

```bash
curl -X POST https://api.lanachat.ai/v1/billing/withdrawals \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"amount_cents": 50000}'
```

```json
{ "id": "wd_118", "status": "pending_review", "amount_cents": 50000, "requested_at": "2026-10-06T15:00:00Z", "tx_hash": null }
```

The earnings are sent in USDC to the wallet on file, 7 days after the month ends. Only affiliate earnings can be withdrawn, never the prepaid balance. A request under $500 is refused.

## Statements

### List statements

`GET /billing/invoices` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `year` | integer | No | For example `2026` |

```bash
curl "https://api.lanachat.ai/v1/billing/invoices?year=2026" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

One invoice object per month.

### Download a statement

`GET /billing/invoices/{id}.pdf` · `owner`

```bash
curl https://api.lanachat.ai/v1/billing/invoices/LC-2026-09.pdf \
  -H "Authorization: Bearer $LANACHAT_TOKEN" -o LC-2026-09.pdf
```

> Warning: Check your statements. A billing error must be reported within 60 days of the statement that shows it; after that, the statement is final.

### Set the billing details

`PUT /billing/details` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `company` | string | No | Company name |
| `email` | string | No | Billing e-mail |
| `address`, `city`, `postal`, `country` | string | No | Postal address |
| `vat` | string | No | VAT number |

```bash
curl -X PUT https://api.lanachat.ai/v1/billing/details \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"company": "Mia Agency Ltd", "email": "billing@mia-agency.com", "address": "12 Market Street", "city": "Dublin", "postal": "D02 X285", "country": "IE", "vat": "IE1234567T"}'
```

The same fields as **Billing › Billing details**. Statements carry these details. `PUT` replaces them all: a field you leave out is emptied.

## Related

- [LanaChat API: quick start, authentication and errors](https://lanachat.ai/guides/api-overview/)
- [Billing: licences, commission and balance](https://lanachat.ai/guides/billing/)
