API reference

Billing API

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.

Planned

Every endpoint on this page is marked Planned: it is the published contract the backend is built from, and it does not answer yet.

Endpoints #

MethodPathDescriptionPermissionStatus
GET/billing/balanceThe balance, this month's net sales and commission tierownerPlanned
GET/billing/ledgerEvery movement: deposits, licences, commission, refundsownerPlanned
GET/billing/ledger.csvThe same ledger as CSV (from=, to=)ownerPlanned
GET/billing/deposit-addressesOne deposit address per chain (with a memo on Solana)ownerPlanned
GET/billing/depositsDeposits and their confirmationsownerPlanned
GET/billing/licencesLicences in stock and assigned, with days leftownerPlanned
POST/billing/licences/buyBuy licences: single or a pack of 10, 25 or 50ownerPlanned
POST/billing/licences/assignGive a licence from stock to an accountownerPlanned
POST/billing/licences/unassignTake it back to stock with its days leftownerPlanned
PATCH/billing/licences/auto-renewTurn auto-renew on or off for an accountownerPlanned
GET/billing/withdrawalsAffiliate withdrawals and their statusownerPlanned
POST/billing/withdrawalsAsk for a withdrawal of affiliate earnings (from $500)ownerPlanned
GET/billing/invoicesMonthly statements (year=)ownerPlanned
GET/billing/invoices/{id}.pdfOne statement as PDFownerPlanned
PUT/billing/detailsCompany name, address and VAT number on statementsownerPlanned

Prices in one table #

WhatPrice
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 month12% of AI sales, 4% of manual sales
Commission from $30k to $100k10% and 3%
Commission above $100k8% 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
}
FieldTypeDescription
balance_centsintegerPrepaid balance, in US cents
monthstringThe calendar month counted, YYYY-MM
net_sales_centsintegerNet sales of the workspace this month
tierobjectThe current tier: its ceiling up_to_cents, the AI rate and the manual rate in basis points
licencesobjectLicences in_stock, assigned, and assigned ones ending within 7 days (expiring_7d)
low_balancebooleantrue 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
}
FieldTypeDescription
idstringThe line
atstringISO 8601, UTC
kindstringdeposit, licences, commission, refund, affiliate or other
labelstringThe text of the line, as in the Console
amount_centsintegerPositive when money comes in, negative when it goes out
balance_after_centsintegerThe 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"
}
FieldTypeDescription
idstringThe deposit
atstringWhen it was seen, ISO 8601
chainstringusdc-eth, usdc-sol, usdt-sol, usdt-tron or usdt-eth
sent_cents, fee_cents, credited_centsintegerWhat you sent, the fee, what lands on the balance
statusstringpending then confirmed
confirmationsstringNetwork 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
}
FieldTypeDescription
account_idstringThe Telegram account the licence runs on
creator_idstringHer creator
days_leftintegerDays before it ends, out of 30
expires_atstringWhen it ends
auto_renewbooleanRenew 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 }
FieldTypeDescription
idstringThe withdrawal
statusstringpending_review, sent (with tx_hash) or rejected with a reason
amount_centsintegerAmount asked, at least 50000 ($500)
requested_atstringISO 8601, UTC
tx_hashstring or nullThe 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"
}
FieldTypeDescription
idstringStatement number, LC-YYYY-MM
monthstringThe month it covers
licences_cents, commission_cents, total_centsintegerLicences, commission and total of the month
statusstringPaid, in progress (the current month) or failed
pdf_urlstringThe PDF of the statement

Balance and ledger #

Get the balance #

GET/billing/balancePlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/balance \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/balance', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/balance",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

The answer is the balance object.

List the ledger #

GET/billing/ledgerPlannedNeeds owner
ParameterTypeRequiredDescription
from, tostringNoDays YYYY-MM-DD, UTC, both included
limit, cursornumber, stringNoPagination
curl "https://api.lanachat.ai/v1/billing/ledger?from=2026-10-01&to=2026-10-06" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/ledger?from=2026-10-01&to=2026-10-06', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/ledger?from=2026-10-01&to=2026-10-06",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())
Response
{
  "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.csvPlannedNeeds owner
ParameterTypeRequiredDescription
from, tostringNoDays YYYY-MM-DD, UTC
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
import { writeFile } from 'node:fs/promises';

const res = await fetch('https://api.lanachat.ai/v1/billing/ledger.csv?from=2026-09-01&to=2026-09-30', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
await writeFile('ledger-2026-09.csv', Buffer.from(await res.arrayBuffer()));
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/ledger.csv?from=2026-09-01&to=2026-09-30",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
with open("ledger-2026-09.csv", "wb") as f:
    f.write(r.content)

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

Deposits #

Get the deposit addresses #

GET/billing/deposit-addressesPlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/deposit-addresses \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/deposit-addresses', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/deposit-addresses",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

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/depositsPlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/deposits \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/deposits', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/deposits",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())
Response
{
  "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/licencesPlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/licences \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/licences', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/licences",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

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

Buy licences #

POST/billing/licences/buyPlannedNeeds owner
ParameterTypeRequiredDescription
packintegerYes1 (single, $19), 10 ($170), 25 ($375) or 50 ($650)
qtyintegerYesHow many of that pack
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}'
const res = await fetch('https://api.lanachat.ai/v1/billing/licences/buy', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ pack: 10, qty: 1 }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import uuid
import requests

r = requests.post(
    "https://api.lanachat.ai/v1/billing/licences/buy",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"pack": 10, "qty": 1},
    timeout=30,
)
r.raise_for_status()
print(r.json())
Response
{ "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/assignPlannedNeeds owner
ParameterTypeRequiredDescription
account_idstringYesThe Telegram account that gets a licence
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"}'
const res = await fetch('https://api.lanachat.ai/v1/billing/licences/assign', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ account_id: 'acc_1001' }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import uuid
import requests

r = requests.post(
    "https://api.lanachat.ai/v1/billing/licences/assign",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"account_id": "acc_1001"},
    timeout=30,
)
r.raise_for_status()
print(r.json())

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/unassignPlannedNeeds owner
ParameterTypeRequiredDescription
account_idstringYesThe account that gives its licence back
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"}'
const res = await fetch('https://api.lanachat.ai/v1/billing/licences/unassign', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ account_id: 'acc_1001' }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import uuid
import requests

r = requests.post(
    "https://api.lanachat.ai/v1/billing/licences/unassign",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"account_id": "acc_1001"},
    timeout=30,
)
r.raise_for_status()
print(r.json())

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-renewPlannedNeeds owner
ParameterTypeRequiredDescription
account_idstringYesThe account
auto_renewbooleanYestrue to renew it when its days run out
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}'
const res = await fetch('https://api.lanachat.ai/v1/billing/licences/auto-renew', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ account_id: 'acc_1001', auto_renew: false }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.patch(
    "https://api.lanachat.ai/v1/billing/licences/auto-renew",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    json={"account_id": "acc_1001", "auto_renew": False},
    timeout=30,
)
r.raise_for_status()
print(r.json())

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.

Affiliate withdrawals #

List withdrawals #

GET/billing/withdrawalsPlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/withdrawals \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/withdrawals', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/withdrawals",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

A list of withdrawal objects, newest first.

Ask for a withdrawal #

POST/billing/withdrawalsPlannedNeeds owner
ParameterTypeRequiredDescription
amount_centsintegerYesAt least 50000 ($500), at most what is available to withdraw
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}'
const res = await fetch('https://api.lanachat.ai/v1/billing/withdrawals', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ amount_cents: 50000 }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import uuid
import requests

r = requests.post(
    "https://api.lanachat.ai/v1/billing/withdrawals",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"amount_cents": 50000},
    timeout=30,
)
r.raise_for_status()
print(r.json())
Response
{ "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/invoicesPlannedNeeds owner
ParameterTypeRequiredDescription
yearintegerNoFor example 2026
curl "https://api.lanachat.ai/v1/billing/invoices?year=2026" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/billing/invoices?year=2026', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/invoices?year=2026",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

One invoice object per month.

Download a statement #

GET/billing/invoices/{id}.pdfPlannedNeeds owner
curl https://api.lanachat.ai/v1/billing/invoices/LC-2026-09.pdf \
  -H "Authorization: Bearer $LANACHAT_TOKEN" -o LC-2026-09.pdf
import { writeFile } from 'node:fs/promises';

const res = await fetch('https://api.lanachat.ai/v1/billing/invoices/LC-2026-09.pdf', {
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
await writeFile('LC-2026-09.pdf', Buffer.from(await res.arrayBuffer()));
import os
import requests

r = requests.get(
    "https://api.lanachat.ai/v1/billing/invoices/LC-2026-09.pdf",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    timeout=30,
)
r.raise_for_status()
with open("LC-2026-09.pdf", "wb") as f:
    f.write(r.content)
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/detailsPlannedNeeds owner
ParameterTypeRequiredDescription
companystringNoCompany name
emailstringNoBilling e-mail
address, city, postal, countrystringNoPostal address
vatstringNoVAT number
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": "[email protected]", "address": "12 Market Street", "city": "Dublin", "postal": "D02 X285", "country": "IE", "vat": "IE1234567T"}'
const res = await fetch('https://api.lanachat.ai/v1/billing/details', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    company: 'Mia Agency Ltd',
    email: '[email protected]',
    address: '12 Market Street',
    city: 'Dublin',
    postal: 'D02 X285',
    country: 'IE',
    vat: 'IE1234567T',
  }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
import os
import requests

r = requests.put(
    "https://api.lanachat.ai/v1/billing/details",
    headers={
        "Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
    },
    json={
        "company": "Mia Agency Ltd",
        "email": "[email protected]",
        "address": "12 Market Street",
        "city": "Dublin",
        "postal": "D02 X285",
        "country": "IE",
        "vat": "IE1234567T",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

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

Errors #

Every endpoint answers errors in the same JSON body, {"error": {"code": "…", "message": "…"}}, with the common codes 400, 401, 402, 403, 404, 409, 429 and 500 listed in Errors. What the endpoints of this page say they answer:

EndpointStatusWhen
Buy licences402A balance too low answers 402 (insufficient_balance).

Tutorial: API: balance, licences, withdrawals and invoices