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.

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/brainsList brains (creator_id=)can_enginePlanned
POST/brainsCreate a brain for a creator, empty or copied from another (copy_from)can_enginePlanned
GET/brains/{id}The live version of a braincan_enginePlanned
PATCH/brains/{id}Rename it or link it to another creatorcan_enginePlanned
DELETE/brains/{id}Delete a brain no account usescan_enginePlanned
GET/brains/{id}/draftThe current draft and what differs from livecan_enginePlanned
PATCH/brains/{id}/draftChange the draft (phases, rules, settings)can_enginePlanned
DELETE/brains/{id}/draftThrow the draft awaycan_enginePlanned
POST/brains/{id}/publishPublish the draft; it becomes live on every linked accountcan_publishPlanned
GET/brains/{id}/versionsPublished versions, newest firstcan_enginePlanned
POST/brains/{id}/versions/{version}/restoreCopy an old version into the draftcan_enginePlanned
POST/playground/{brain_id}/messageSend a test message to the draft and get the AI's replycan_enginePlanned
DELETE/playground/{brain_id}/sessionStart the test conversation overcan_enginePlanned

The brain object #

JSON
{
  "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 }
  ]
}
FieldTypeDescription
idstringStarts with br_
namestringIts name in the Console
creator_idstringThe creator it is linked to
versionnumberThe live version; it goes up by one on each publish
published_atstring or nullWhen the live version was published, ISO 8601 UTC
has_draftbooleantrue when a draft differs from the live version
accountsarray of stringsTelegram accounts that answer with this brain
phasesarraySales phases: key, name and whether each one is enabled

Brains #

List brains #

GET/brainsPlannedNeeds can_engine
ParameterTypeRequiredDescription
creator_idstringNoOnly this creator's brains
limit, cursornumber, stringNoPagination
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())
Response
{
  "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 #

POST/brainsPlannedNeeds can_engine
ParameterTypeRequiredDescription
creator_idstringYesThe creator it is for
namestringNoIts name in the Console
copy_fromstringNoId 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 #

GET/brains/{id}PlannedNeeds can_engine
curl 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.

Delete a brain #

DELETE/brains/{id}PlannedNeeds can_engine
curl -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 #

GET/brains/{id}/draftPlannedNeeds can_engine
curl 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 #

PATCH/brains/{id}/draftPlannedNeeds can_engine
ParameterTypeRequiredDescription
phasesarrayNoSales phases to change
rulesarrayNoRules to change
settingsobjectNoSettings 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 #

DELETE/brains/{id}/draftPlannedNeeds can_engine
curl -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 #

POST/brains/{id}/publishPlannedNeeds can_publish
curl -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())
Response
{ "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 #

GET/brains/{id}/versionsPlannedNeeds can_engine
curl 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 #

POST/brains/{id}/versions/{version}/restorePlannedNeeds can_engine
curl -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 #

POST/playground/{brain_id}/messagePlannedNeeds can_engine
ParameterTypeRequiredDescription
textstringYesWhat the test fan writes
fanobjectNoThe 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())
Response
{
  "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."
}
  • reply is what the AI would send: text and media.
  • phase is the sales phase the AI is in.
  • explain says in plain English why it answered that way.

Each call continues the same test conversation.

Start the test conversation over #

DELETE/playground/{brain_id}/sessionPlannedNeeds can_engine
curl -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:

EndpointStatusWhen
Create a brain404A copy_from brain that does not exist in this workspace answers 404.
Delete a brain409A brain that an account still uses is refused (409): link those accounts to another brain first.
Publish the draft409Publishing without a draft answers 409.
Restore a version404An unknown version answers 404.

Tutorial: API: AI Brains, drafts and the Playground