LanaChat API: quick start, authentication and errors
Everything your team does in the Console, your code can do too. The LanaChat API reads and changes your whole workspace: creators, Telegram accounts, AI Brains, scripts, the Vault, fans and conversations, tracking links, billing and your team. It is plain JSON over HTTPS, one URL per resource, with the same permissions as the Console. Webhooks push events to your server, and the MCP server lets an AI assistant such as Claude or Cursor work in your workspace with the same token.
Note This is the public API reference. Endpoints marked Planned are the published contract the backend is built from: they do not answer yet. The MCP connection uses the same access tokens.
What you can build #
- A sales dashboard in your own tools: today's revenue per creator, per link, per source.
- Alerts in Slack or Telegram when a fan buys, when a conversation needs review, or when an account loses its connection.
- A setup script that adds a creator, connects her accounts, uploads her Vault and writes her first script.
- A tracking pipeline: create one link per ad, read clicks, conversations and buyers every hour.
- An AI assistant that drafts scripts and persona changes for you to review, through MCP.
Quick start in 5 minutes #
- In the Console, open Users › API & MCP and press Create token.
- Name it (for example "Reporting script"), choose Custom permissions and tick what it needs, for example Read fans and conversations and Read sales.
- Save, then press Copy token. The token is shown once.
4. Keep it in an environment variable, so it never sits in your code:
export LANACHAT_TOKEN="lc_live_8f2c1d0e9b7a4c6f"
5. Make your first call. It returns your workspace and what the token may do:
curl https://api.lanachat.ai/v1/workspace \
-H "Authorization: Bearer $LANACHAT_TOKEN"
const res = await fetch('https://api.lanachat.ai/v1/workspace', {
headers: { Authorization: `Bearer ${process.env.LANACHAT_TOKEN}` },
});
if (!res.ok) throw new Error((await res.json()).error.message);
const workspace = await res.json();
console.log(workspace.name, workspace.token.permissions);
import os
import requests
r = requests.get(
"https://api.lanachat.ai/v1/workspace",
headers={"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}"},
timeout=30,
)
r.raise_for_status()
print(r.json()["name"])
{
"id": "ws_7Hq2",
"name": "Mia Agency",
"kind": "agency",
"timezone": "Europe/Paris",
"token": { "id": "tok_31", "name": "Reporting script", "permissions": ["can_fan_read", "can_sales_read"], "expires_at": "2027-01-06T00:00:00Z" }
}
That's it: every other endpoint works the same way. Ready-made scripts are in API quick recipes.
Every resource #
| Resource | What it covers | Guide |
|---|---|---|
| Workspace | GET /workspace: the workspace and the token's permissions | This page |
| Creators | Creators, identity, persona and setup progress | Creators |
| Telegram accounts | Accounts, pools, pool settings and stories | Telegram accounts |
| AI Brains | Brains, drafts, publishing, versions and the Playground | AI Brains |
| Scripts | Scripts, steps and folders | Scripts |
| Vault | Media, folders and signed uploads | Vault |
| Fans and conversations | Fans, lists, sales of a fan, messages, takeover | Conversations |
| Tracking | Links, domains, pixels, events and stats | Tracking |
| Billing | Balance, ledger, deposits, licences, withdrawals, invoices | Billing |
| Team | Members, roles and access tokens | Team |
| Webhooks | Events pushed to your server, signatures and retries | Webhooks |
| MCP | The server for AI assistants and its tools | MCP |
Base URL and versions #
Every path in these guides is relative to:
https://api.lanachat.ai/v1
The version is in the path. A breaking change gets a new version (/v2); new fields and new endpoints are added to /v1 without notice, so ignore the fields you do not know.
Authentication #
Every request carries your token in the Authorization header, after the word Bearer:
Authorization: Bearer lc_live_8f2c1d0e9b7a4c6f
- Create, edit, rotate and revoke tokens in Users › API & MCP, or with the token endpoints. Only the workspace owner and agency owners can open that page.
- A token is shown once, when you create it. Store it like a password; Rotate or Revoke it from the same page.
- A request without a valid token answers
401. A token without the permission an endpoint needs answers403. - Tokens never go in a URL or in browser code: call the API from your server.
- Use one token per script or assistant, so each can be changed or revoked on its own.
Workspaces #
A token belongs to one workspace, the one it was created in. Every request reads and writes that workspace only; there is no workspace id in the paths. An owner token (the workspace that manages several agencies) may act on one of its agencies with the header LanaChat-Workspace: ws_…; any other token sending that header gets 403.
Permissions #
Each endpoint lists the permission it needs. They are the ones you tick when you create a token:
| Permission | In the Console | Gives |
|---|---|---|
can_setup | Manage workspace setup | Creators, media, Telegram accounts, scripts and team members |
can_engine | Access AI configuration | AI Brain drafts, scripts' AI settings and tests |
can_publish | Publish AI Brain drafts | Publishing a brain draft (needs can_engine) |
can_ai_test | Test Advanced AI drafts | Trying drafts that use Advanced AI (owner workspace only) |
can_ai_tune | Adjust Advanced AI settings | Changing the Advanced AI sensitivity settings (owner workspace only) |
can_ai_publish | Publish Advanced AI profiles | Publishing profiles that use Advanced AI (owner workspace only) |
can_commercial_reports | Read commercial experiment reports | Experiments and their review reports (owner workspace only) |
can_fan_read | Read fans and conversations | Fans, conversations and messages, read only |
can_fan_import | Import fan history | Importing past fans and messages |
can_sales_read | Read sales | Sales, stats and the dashboard numbers |
can_chat | New: Chat with fans | Sending messages, reactions and taking over a conversation |
can_tracking | New: Manage tracking | Links, domains, pools, pixels and events |
owner | Owner token | Billing, roles, webhooks and access tokens; only the workspace owner can create it |
- Turning Access AI configuration (
can_engine) off also turns off publishing, Advanced AI and experiment reports. can_chat,can_trackingand theownertoken are planned: the Console does not offer them yet.
Requests and responses #
- Bodies are JSON (
Content-Type: application/json), in UTF-8. Uploads go through a signed URL, never through the API itself. - Ids are strings with a prefix that says what they are:
cr_creator,acc_Telegram account,pool_,sto_story,br_brain,scr_script,med_media,fan_,conv_conversation,msg_,lnk_link,px_pixel,mem_member,tok_token. - Times are ISO 8601 in UTC (
2026-10-06T14:05:00Z). Days in stats are UTC days. - Money is in US cents (
amount_cents: 1900is $19.00). Telegram Stars are whole numbers (stars: 250). PATCHchanges only the fields you send.PUTreplaces the whole object.- A
POSTthat creates something answers201with the new object; other successful calls answer200.
Pagination #
Every list answers one page and a cursor to the next one:
curl "https://api.lanachat.ai/v1/fans?limit=50&cursor=eyJpZCI6ImZhbl85MjAifQ" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
{
"data": [ { "id": "fan_921", "display_name": "Marco", "creator_id": "cr_lea" } ],
"next_cursor": "eyJpZCI6ImZhbl85NzAifQ"
}
limitis 1 to 100, 25 by default.next_cursorisnullon the last page. A cursor is opaque: pass it back as it is, never build one.- Lists are sorted newest first unless the endpoint says otherwise.
To read every page, loop until next_cursor is null:
async function all(path) {
const items = [];
let cursor = null;
do {
const url = new URL('https://api.lanachat.ai/v1' + path);
url.searchParams.set('limit', '100');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.LANACHAT_TOKEN}` } });
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
items.push(...page.data);
cursor = page.next_cursor;
} while (cursor);
return items;
}
const fans = await all('/fans?creator_id=cr_lea');
import os
import requests
def all_items(path, **params):
items, cursor = [], None
while True:
r = requests.get(
"https://api.lanachat.ai/v1" + path,
params={**params, "limit": 100, **({"cursor": cursor} if cursor else {})},
headers={"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}"},
timeout=30,
)
r.raise_for_status()
page = r.json()
items += page["data"]
cursor = page["next_cursor"]
if not cursor:
return items
fans = all_items("/fans", creator_id="cr_lea")
Errors #
An error answers a 4xx or 5xx status and always the same body:
{
"error": {
"code": "insufficient_balance",
"message": "Your balance is $12.40 and this pack costs $170.00. Top up first."
}
}
| Status | code | When |
|---|---|---|
| 400 | invalid_request | A field is missing or has the wrong format; message says which |
| 401 | unauthorized | No token, or a revoked or expired token |
| 402 | insufficient_balance | The balance does not cover a purchase |
| 403 | forbidden | The token lacks the permission the endpoint needs |
| 404 | not_found | The id does not exist in this workspace |
| 409 | conflict | The change would break a rule (a duplicate slug, the last sending account of a creator) |
| 429 | rate_limited | Too many requests; wait for Retry-After seconds |
| 500 | server_error | Our side failed; retry later with the same Idempotency-Key |
message is a plain English sentence you can show to a person. Branch your code on code, never on message.
Rate limits #
Each token may send 600 requests per minute. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the window resets). Above the limit the API answers 429 with a Retry-After header. Sending a message to a fan is also bound by Telegram's own limits: see the pool rules.
A small helper that waits and tries again on 429 and 5xx:
async function lanachat(method, path, body) {
const key = crypto.randomUUID(); // one key per action: a retry never does the work twice
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch('https://api.lanachat.ai/v1' + path, {
method,
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
...(method === 'POST' ? { 'Idempotency-Key': key } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 429 || res.status >= 500) {
const wait = Number(res.headers.get('Retry-After')) || 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
const data = await res.json();
if (!res.ok) throw Object.assign(new Error(data.error.message), { code: data.error.code, status: res.status });
return data;
}
throw new Error('LanaChat API: too many retries');
}
Use it everywhere: await lanachat('GET', '/creators'), await lanachat('POST', '/links', { … }).
Idempotency #
Send an Idempotency-Key header (any unique string up to 255 characters, a UUID is fine) on every POST. If the same key comes back within 24 hours, the API answers the first result again instead of doing the work twice: a network error never sends a message or buys a pack twice.
curl -X POST https://api.lanachat.ai/v1/billing/licences/buy \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: 4b7f2a8e-1c3d-4e5f-9a0b-6c7d8e9f0a1b" \
-H "Content-Type: application/json" \
-d '{"pack": 10, "qty": 1}'
The same key with a different body answers 409. In a shell, $(uuidgen) makes a fresh key for each new action.
Webhooks #
Instead of asking the API every minute, let LanaChat call you. A webhook sends an HTTPS POST to your server when a fan writes for the first time, buys, is refunded, when a media is ready or an account loses its connection. Every delivery is signed (HMAC-SHA256) and retried for 24 hours. The events, payloads and the signature check in Node and Python are in Webhooks.
MCP for AI assistants #
MCP is an open standard that lets an AI assistant use tools in an app. LanaChat's MCP server lets Claude, Cursor or any MCP client read your creators, draft scripts, edit a brain draft and read your sales, with the same access token and the same permissions. In Claude Code, one command connects it:
claude mcp add --transport http lanachat https://app.lanachat.ai/mcp \
--header "Authorization: Bearer $LANACHAT_TOKEN"
The configuration for every assistant and the list of tools are in API & MCP.