API reference
AI Brains API
An AI Brain holds how the AI sells for a creator: its sales phases, rules and settings. You change a draft, test it in the Playground, then publish it; the live brain only changes on publish. A brain is linked to one creator and can be reused by her accounts.
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 #
| Method | Path | Description | Permission | Status |
|---|---|---|---|---|
| GET | /brains | List brains (creator_id=) | can_engine | Planned |
| POST | /brains | Create a brain for a creator, empty or copied from another (copy_from) | can_engine | Planned |
| GET | /brains/{id} | The live version of a brain | can_engine | Planned |
| PATCH | /brains/{id} | Rename it or link it to another creator | can_engine | Planned |
| DELETE | /brains/{id} | Delete a brain no account uses | can_engine | Planned |
| GET | /brains/{id}/draft | The current draft and what differs from live | can_engine | Planned |
| PATCH | /brains/{id}/draft | Change the draft (phases, rules, settings) | can_engine | Planned |
| DELETE | /brains/{id}/draft | Throw the draft away | can_engine | Planned |
| POST | /brains/{id}/publish | Publish the draft; it becomes live on every linked account | can_publish | Planned |
| GET | /brains/{id}/versions | Published versions, newest first | can_engine | Planned |
| POST | /brains/{id}/versions/{version}/restore | Copy an old version into the draft | can_engine | Planned |
| POST | /playground/{brain_id}/message | Send a test message to the draft and get the AI's reply | can_engine | Planned |
| DELETE | /playground/{brain_id}/session | Start the test conversation over | can_engine | Planned |
The brain object #
{
"id": "br_lea_main",
"name": "Lea · main",
"creator_id": "cr_lea",
"version": 12,
"published_at": "2026-10-04T21:10:00Z",
"has_draft": true,
"accounts": ["acc_1001", "acc_1004"],
"phases": [
{ "key": "warm_up", "name": "Warm up", "enabled": true },
{ "key": "first_offer", "name": "First offer", "enabled": true }
]
}| Field | Type | Description |
|---|---|---|
id | string | Starts with br_ |
name | string | Its name in the Console |
creator_id | string | The creator it is linked to |
version | number | The live version; it goes up by one on each publish |
published_at | string or null | When the live version was published, ISO 8601 UTC |
has_draft | boolean | true when a draft differs from the live version |
accounts | array of strings | Telegram accounts that answer with this brain |
phases | array | Sales phases: key, name and whether each one is enabled |
Brains #
List brains #
/brainsPlannedNeeds can_engine| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | No | Only this creator's brains |
limit, cursor | number, string | No | Pagination |
curl "https://api.lanachat.ai/v1/brains?creator_id=cr_lea" \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains?creator_id=cr_lea', {
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/brains?creator_id=cr_lea",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json()){
"data": [
{ "id": "br_lea_main", "name": "Lea · main", "creator_id": "cr_lea", "version": 12, "published_at": "2026-10-04T21:10:00Z", "has_draft": true, "accounts": ["acc_1001", "acc_1004"], "phases": [ { "key": "warm_up", "name": "Warm up", "enabled": true }, { "key": "first_offer", "name": "First offer", "enabled": true } ] }
],
"next_cursor": null
}Create a brain #
/brainsPlannedNeeds can_engine| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator it is for |
name | string | No | Its name in the Console |
copy_from | string | No | Id of a brain to copy; without it the brain starts empty |
curl -X POST https://api.lanachat.ai/v1/brains \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"creator_id": "cr_lea", "name": "Lea · TikTok test", "copy_from": "br_lea_main"}'const res = await fetch('https://api.lanachat.ai/v1/brains', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({ creator_id: 'cr_lea', name: 'Lea · TikTok test', copy_from: 'br_lea_main' }),
});
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/brains",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"creator_id": "cr_lea", "name": "Lea · TikTok test", "copy_from": "br_lea_main"},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the new brain (201). A copy_from brain that does not exist in this workspace answers 404.
Get a brain #
/brains/{id}PlannedNeeds can_enginecurl https://api.lanachat.ai/v1/brains/br_lea_main \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main', {
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/brains/br_lea_main",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the live version, as the brain object. The draft is read with GET /brains/{id}/draft.
Rename or relink a brain #
/brains/{id}PlannedNeeds can_engine| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | New name |
creator_id | string | No | Link it to another creator |
curl -X PATCH https://api.lanachat.ai/v1/brains/br_lea_main \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Lea · main FR"}'const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Lea · main FR' }),
});
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/brains/br_lea_main",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
json={"name": "Lea · main FR"},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the updated brain.
Delete a brain #
/brains/{id}PlannedNeeds can_enginecurl -X DELETE https://api.lanachat.ai/v1/brains/br_lea_old \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_old', {
method: 'DELETE',
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.delete(
"https://api.lanachat.ai/v1/brains/br_lea_old",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())A brain that an account still uses is refused (409): link those accounts to another brain first.
Drafts and publishing #
Read the draft #
/brains/{id}/draftPlannedNeeds can_enginecurl https://api.lanachat.ai/v1/brains/br_lea_main/draft \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/draft', {
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/brains/br_lea_main/draft",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the current draft (phases, rules and settings) and what differs from the live version.
Change the draft #
/brains/{id}/draftPlannedNeeds can_engine| Parameter | Type | Required | Description |
|---|---|---|---|
phases | array | No | Sales phases to change |
rules | array | No | Rules to change |
settings | object | No | Settings to change, for example first_offer_after_messages or max_discount_percent |
curl -X PATCH https://api.lanachat.ai/v1/brains/br_lea_main/draft \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"settings": {"first_offer_after_messages": 6, "max_discount_percent": 20}}'const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/draft', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ settings: { first_offer_after_messages: 6, max_discount_percent: 20 } }),
});
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/brains/br_lea_main/draft",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
json={"settings": {"first_offer_after_messages": 6, "max_discount_percent": 20}},
timeout=30,
)
r.raise_for_status()
print(r.json())Only the fields you send change. The live brain does not move until you publish.
Throw the draft away #
/brains/{id}/draftPlannedNeeds can_enginecurl -X DELETE https://api.lanachat.ai/v1/brains/br_lea_main/draft \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/draft', {
method: 'DELETE',
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.delete(
"https://api.lanachat.ai/v1/brains/br_lea_main/draft",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The draft is deleted; the live version stays as it is and has_draft becomes false.
Publish the draft #
/brains/{id}/publishPlannedNeeds can_publishcurl -X POST https://api.lanachat.ai/v1/brains/br_lea_main/publish \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/publish', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
},
});
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/brains/br_lea_main/publish",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
r.raise_for_status()
print(r.json()){ "id": "br_lea_main", "version": 13, "published_at": "2026-10-06T14:20:03Z", "has_draft": false }The draft becomes live on every linked account. Publishing without a draft answers 409. Advanced AI profiles need their own owner permissions, as in the Console. A webhook brain.published tells your server.
Versions #
List versions #
/brains/{id}/versionsPlannedNeeds can_enginecurl https://api.lanachat.ai/v1/brains/br_lea_main/versions \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/versions', {
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/brains/br_lea_main/versions",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer lists the published versions, newest first, paginated.
Restore a version #
/brains/{id}/versions/{version}/restorePlannedNeeds can_enginecurl -X POST https://api.lanachat.ai/v1/brains/br_lea_main/versions/11/restore \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"const res = await fetch('https://api.lanachat.ai/v1/brains/br_lea_main/versions/11/restore', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
},
});
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/brains/br_lea_main/versions/11/restore",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
r.raise_for_status()
print(r.json())The old version is copied into the draft. Nothing changes for fans until you publish. An unknown version answers 404.
Playground #
The Playground talks to the draft, never to a fan. Nothing is sent on Telegram and nothing is sold.
Send a test message #
/playground/{brain_id}/messagePlannedNeeds can_engine| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | Yes | What the test fan writes |
fan | object | No | The test fan: language (for example en) and is_new |
curl -X POST https://api.lanachat.ai/v1/playground/br_lea_main/message \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"text": "hey, are you real?", "fan": {"language": "en", "is_new": true}}'const res = await fetch('https://api.lanachat.ai/v1/playground/br_lea_main/message', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'hey, are you real?', fan: { language: 'en', is_new: true } }),
});
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/playground/br_lea_main/message",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"text": "hey, are you real?", "fan": {"language": "en", "is_new": True}},
timeout=30,
)
r.raise_for_status()
print(r.json()){
"reply": { "text": "Haha very real, just slow at typing 🙈 where are you from?", "media": [] },
"phase": "warm_up",
"explain": "Phrase bank answer for \"Are you a bot?\", then a question to keep the fan talking."
}replyis what the AI would send: text and media.phaseis the sales phase the AI is in.explainsays in plain English why it answered that way.
Each call continues the same test conversation.
Start the test conversation over #
/playground/{brain_id}/sessionPlannedNeeds can_enginecurl -X DELETE https://api.lanachat.ai/v1/playground/br_lea_main/session \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/playground/br_lea_main/session', {
method: 'DELETE',
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.delete(
"https://api.lanachat.ai/v1/playground/br_lea_main/session",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The next test message starts a new conversation, as a new fan.
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:
| Endpoint | Status | When |
|---|---|---|
| Create a brain | 404 | A copy_from brain that does not exist in this workspace answers 404. |
| Delete a brain | 409 | A brain that an account still uses is refused (409): link those accounts to another brain first. |
| Publish the draft | 409 | Publishing without a draft answers 409. |
| Restore a version | 404 | An unknown version answers 404. |