API reference
Scripts API
A script is a fixed sequence of steps for one creator: each step is a text, optional media from the Vault, a price in Stars and a wait before the next one. Scripts sit in folders. One script per creator can be marked first: it runs on new conversations.
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 | /scripts | List scripts (creator_id= required, folder_id=, active=true) | can_setup | Planned |
| POST | /scripts | Create a script with its steps | can_setup | Planned |
| GET | /scripts/{id} | One script and its steps | can_setup | Planned |
| PUT | /scripts/{id} | Replace a script (name, steps, folder, accounts) | can_setup | Planned |
| PATCH | /scripts/{id} | Turn it on or off, mark it first, move it to a folder | can_setup | Planned |
| POST | /scripts/{id}/duplicate | Copy a script (the copy is never first) | can_setup | Planned |
| DELETE | /scripts/{id} | Delete a script | can_setup | Planned |
| GET | /scripts/folders | Folders of a creator (creator_id=) | can_setup | Planned |
| POST | /scripts/folders | Create a folder | can_setup | Planned |
| PATCH | /scripts/folders/{id} | Rename a folder | can_setup | Planned |
| DELETE | /scripts/folders/{id} | Delete a folder; its scripts move to "No folder" | can_setup | Planned |
| POST | /conversations/{id}/script | Run a script on one conversation (see Conversations) | can_chat | Planned |
The script object #
{
"id": "scr_204",
"creator_id": "cr_lea",
"name": "Welcome message",
"goal": "First sale within the first day",
"folder_id": "fld_12",
"active": true,
"first": true,
"account_ids": null,
"steps": [
{ "id": "st_1", "name": "Hello", "text": "Hey you 😊 glad you found me here", "media": [], "wait_minutes": 0, "pricing": { "mode": "free" } },
{ "id": "st_2", "name": "First set", "text": "Made this one this morning…", "media": ["med_5521", "med_5522"], "wait_minutes": 20, "pricing": { "mode": "paid", "stars": 250, "discount_stars": 200 } }
],
"created_at": "2026-09-30T10:00:00Z",
"updated_at": "2026-10-05T16:22:00Z"
}| Field | Type | Description |
|---|---|---|
id | string | Starts with scr_ |
creator_id | string | The creator whose fans receive it |
name | string | Its name in the Console |
goal | string or null | What the script is for, in your words |
folder_id | string or null | Its folder; null is "No folder" |
active | boolean | false means it never runs |
first | boolean | true means it runs on new conversations |
account_ids | array or null | null means every Telegram account of the creator; an array means only those (at least one) |
steps | array | The steps, in order (see below) |
created_at, updated_at | string | ISO 8601, UTC |
Each step has:
| Field | Type | Description |
|---|---|---|
id | string | Starts with st_ |
name | string | Its name in the editor |
text | string | The message the fan receives |
media | array of strings | Media ids from the creator's Vault |
wait_minutes | number | Wait before this step is sent |
pricing.mode | string | free, vault (each media at its Vault price) or paid (one price for the whole step) |
pricing.stars | number | The price in Stars, when mode is paid |
pricing.discount_stars | number | Optional lower price in Stars |
Setting first: true on a script removes it from the creator's other scripts.
Scripts #
List scripts #
/scriptsPlannedNeeds can_setup| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator |
folder_id | string | No | Only the scripts of this folder |
active | boolean | No | true lists only the scripts that are on |
limit, cursor | number, string | No | Pagination |
curl "https://api.lanachat.ai/v1/scripts?creator_id=cr_lea&active=true" \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/scripts?creator_id=cr_lea&active=true', {
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/scripts?creator_id=cr_lea&active=true",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is a page of script objects in data, with next_cursor. Without creator_id the API answers 400.
Create a script #
/scriptsPlannedNeeds can_setup| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator |
name | string | Yes | Its name |
steps | array | Yes | The steps, as in the object above (without id) |
goal | string | No | What it is for |
folder_id | string | No | A folder of the same creator |
account_ids | array of strings | No | Only these accounts; leave it out for all |
curl -X POST https://api.lanachat.ai/v1/scripts \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"creator_id": "cr_lea", "name": "Win back", "steps": [{"name": "Miss you", "text": "Hey stranger, where did you go?", "wait_minutes": 0, "pricing": {"mode": "free"}}]}'const res = await fetch('https://api.lanachat.ai/v1/scripts', {
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: 'Win back',
steps: [{
name: 'Miss you',
text: 'Hey stranger, where did you go?',
wait_minutes: 0,
pricing: { mode: 'free' },
}],
}),
});
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/scripts",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"creator_id": "cr_lea",
"name": "Win back",
"steps": [{
"name": "Miss you",
"text": "Hey stranger, where did you go?",
"wait_minutes": 0,
"pricing": {"mode": "free"},
}],
},
timeout=30,
)
r.raise_for_status()
print(r.json()){
"id": "scr_231",
"creator_id": "cr_lea",
"name": "Win back",
"goal": null,
"folder_id": null,
"active": true,
"first": false,
"account_ids": null,
"steps": [ { "id": "st_1", "name": "Miss you", "text": "Hey stranger, where did you go?", "media": [], "wait_minutes": 0, "pricing": { "mode": "free" } } ],
"created_at": "2026-10-06T14:30:00Z",
"updated_at": "2026-10-06T14:30:00Z"
}The answer is the new script (201), active, not first, in no folder. A media id from another creator's Vault is refused (400).
Get a script #
/scripts/{id}PlannedNeeds can_setupcurl https://api.lanachat.ai/v1/scripts/scr_204 \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/scripts/scr_204', {
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/scripts/scr_204",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the script object with all its steps.
Replace a script #
/scripts/{id}PlannedNeeds can_setupSend the whole script: name, steps, folder_id and account_ids, as in Create a script. Steps you leave out are removed.
curl -X PUT https://api.lanachat.ai/v1/scripts/scr_231 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Win back", "folder_id": "fld_12", "account_ids": null, "steps": [{"name": "Miss you", "text": "Hey stranger, where did you go?", "wait_minutes": 0, "pricing": {"mode": "free"}}, {"name": "Gift", "text": "Kept this one for you", "media": ["med_5522"], "wait_minutes": 30, "pricing": {"mode": "paid", "stars": 200}}]}'const res = await fetch('https://api.lanachat.ai/v1/scripts/scr_231', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Win back',
folder_id: 'fld_12',
account_ids: null,
steps: [{
name: 'Miss you',
text: 'Hey stranger, where did you go?',
wait_minutes: 0,
pricing: { mode: 'free' },
}, {
name: 'Gift',
text: 'Kept this one for you',
media: ['med_5522'],
wait_minutes: 30,
pricing: { mode: 'paid', stars: 200 },
}],
}),
});
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/scripts/scr_231",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
json={
"name": "Win back",
"folder_id": "fld_12",
"account_ids": None,
"steps": [{
"name": "Miss you",
"text": "Hey stranger, where did you go?",
"wait_minutes": 0,
"pricing": {"mode": "free"},
}, {
"name": "Gift",
"text": "Kept this one for you",
"media": ["med_5522"],
"wait_minutes": 30,
"pricing": {"mode": "paid", "stars": 200},
}],
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the updated script. An empty account_ids array answers 400: use null for every account.
Turn on, mark first, move #
/scripts/{id}PlannedNeeds can_setup| Parameter | Type | Required | Description |
|---|---|---|---|
active | boolean | No | Turn it on or off |
first | boolean | No | true makes it the script of new conversations |
folder_id | string or null | No | Move it; null is "No folder" |
curl -X PATCH https://api.lanachat.ai/v1/scripts/scr_231 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"first": true}'const res = await fetch('https://api.lanachat.ai/v1/scripts/scr_231', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ first: true }),
});
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/scripts/scr_231",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
json={"first": True},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the updated script. The creator's previous first script loses first.
Duplicate a script #
/scripts/{id}/duplicatePlannedNeeds can_setupcurl -X POST https://api.lanachat.ai/v1/scripts/scr_204/duplicate \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"const res = await fetch('https://api.lanachat.ai/v1/scripts/scr_204/duplicate', {
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/scripts/scr_204/duplicate",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
r.raise_for_status()
print(r.json())The answer is the copy (201). The copy is never first.
Delete a script #
/scripts/{id}PlannedNeeds can_setupcurl -X DELETE https://api.lanachat.ai/v1/scripts/scr_231 \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/scripts/scr_231', {
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/scripts/scr_231",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())Folders #
List folders #
/scripts/foldersPlannedNeeds can_setup| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator |
curl "https://api.lanachat.ai/v1/scripts/folders?creator_id=cr_lea" \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/scripts/folders?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/scripts/folders?creator_id=cr_lea",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())Create a folder #
/scripts/foldersPlannedNeeds can_setup| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator |
name | string | Yes | Folder name |
curl -X POST https://api.lanachat.ai/v1/scripts/folders \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"creator_id": "cr_lea", "name": "Win back"}'const res = await fetch('https://api.lanachat.ai/v1/scripts/folders', {
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: 'Win back' }),
});
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/scripts/folders",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"creator_id": "cr_lea", "name": "Win back"},
timeout=30,
)
r.raise_for_status()
print(r.json())Rename a folder #
/scripts/folders/{id}PlannedNeeds can_setupcurl -X PATCH https://api.lanachat.ai/v1/scripts/folders/fld_12 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Welcome"}'const res = await fetch('https://api.lanachat.ai/v1/scripts/folders/fld_12', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LANACHAT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Welcome' }),
});
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/scripts/folders/fld_12",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
json={"name": "Welcome"},
timeout=30,
)
r.raise_for_status()
print(r.json())Delete a folder #
/scripts/folders/{id}PlannedNeeds can_setupcurl -X DELETE https://api.lanachat.ai/v1/scripts/folders/fld_12 \
-H "Authorization: Bearer $LANACHAT_TOKEN"const res = await fetch('https://api.lanachat.ai/v1/scripts/folders/fld_12', {
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/scripts/folders/fld_12",
headers={
"Authorization": f"Bearer {os.environ['LANACHAT_TOKEN']}",
},
timeout=30,
)
r.raise_for_status()
print(r.json())Its scripts are kept and move to "No folder".
Run a script on a conversation #
/conversations/{id}/scriptPlannedNeeds can_chatStarts a script on one fan's conversation: state becomes script until it ends. Body, example and rules are in Conversations.
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 |
|---|---|---|
| List scripts | 400 | Without creator_id the API answers 400. |
| Create a script | 400 | A media id from another creator's Vault is refused (400). |
| Replace a script | 400 | An empty account_ids array answers 400: use null for every account. |
Tutorial: API: scripts, steps and folders