LanaChat

API: fans, conversations and messages

7 min readUpdated Markdown

A fan is a Telegram user who wrote to one of your creators. A conversation is the chat between that fan and one Telegram account. Messages are sent by the AI, by a teammate or by a script. These endpoints match Live Convs and Fans in the Console.

Endpoints #

MethodPathDescriptionPermissionStatus
GET/fansList fans (creator_id=, status=, list_id=, q=, spent_min=)can_fan_readPlanned
GET/fans/{id}One fan: languages, spend, status, lists, notescan_fan_readPlanned
PATCH/fans/{id}Change the fan's alias, reply language or notescan_chatPlanned
GET/fans/{id}/salesThe fan's purchasescan_sales_readPlanned
POST/fans/importImport past fans and messages (never sends anything)can_fan_importPlanned
GET/fan-listsLists (VIPs, win back…)can_fan_readPlanned
POST/fan-listsCreate a listcan_chatPlanned
POST/fan-lists/{id}/membersAdd fans to a listcan_chatPlanned
DELETE/fan-lists/{id}/membersRemove fans from a listcan_chatPlanned
GET/conversationsList conversations (creator_id=, account_id=, state=, unread=true)can_fan_readPlanned
GET/conversations/{id}One conversation and who is replyingcan_fan_readPlanned
GET/conversations/{id}/messagesMessages, newest first (translate=en adds a translation)can_fan_readPlanned
POST/conversations/{id}/messagesSend a message as the creator (text, media, offer)can_chatPlanned
POST/conversations/{id}/reactionsAdd or remove a reaction on a fan's messagecan_chatPlanned
POST/conversations/{id}/takeoverA person takes over: the AI stops answering this fancan_chatPlanned
POST/conversations/{id}/releaseGive the conversation back to the AIcan_chatPlanned
POST/conversations/{id}/scriptRun a script on this conversationcan_chatPlanned
POST/conversations/{id}/readMark the conversation as readcan_chatPlanned

The fan object #

{
  "id": "fan_921",
  "display_name": "Marco",
  "username": "marco_dl",
  "creator_id": "cr_lea",
  "purchases": 9,
  "spent": { "stars": 1450, "net_cents": 1885 },
  "lists": ["vip"]
}
FieldTypeDescription
idstringStarts with fan_
display_namestringThe fan's name on Telegram
usernamestring or nullThe fan's Telegram @username, without the @
creator_idstringThe creator this fan wrote to
purchasesnumberVerified purchases so far
spentobjectTotal spent: stars, and net_cents in US cents after Telegram's share and refunds
listsarray of stringsIds of the fan lists this fan is in

A fan also carries the languages they write in, their status tag, the team's notes, an alias and a reply language, as on the Fans page.

The conversation object #

{
  "id": "conv_1001_88231",
  "fan_id": "fan_921",
  "creator_id": "cr_lea",
  "account_id": "acc_1001",
  "state": "ai",
  "status": { "tag": "active", "reason": null },
  "unread": 2,
  "last_message_at": "2026-10-06T14:02:51Z",
  "spent": { "stars": 1450, "net_cents": 1885 },
  "source_link_id": "lnk_3"
}
FieldTypeDescription
idstringStarts with conv_
fan_id, creator_id, account_idstringWho talks to whom, on which Telegram account
statestringWho replies: ai, human (taken over), script (a script is running) or off
statusobjecttag (the status tags of the Fans page) and the reason when there is one
unreadnumberFan messages not read yet
last_message_atstringISO 8601, UTC
spentobjectWhat the fan spent in this conversation: stars and net_cents
source_link_idstring or nullThe tracking link the fan came from

status.tag matches the status tags of the Fans page (review, safety_hold, converted, lost, underage…). An off conversation for a safety reason cannot be released to the AI (409).

The message object #

{
  "id": "msg_77120",
  "from": "creator",
  "sent_by": "ai",
  "text": "Made this one this morning…",
  "media": [ { "id": "med_5521", "locked": true, "stars": 300 } ],
  "at": "2026-10-06T14:02:51Z"
}
FieldTypeDescription
idstringStarts with msg_
fromstringcreator or fan
sent_bystringFor creator messages: ai, human or script
member_idstringThe teammate who sent it, when sent_by is human from the Console
token_idstringThe access token that sent it, when it came from the API
textstringThe text of the message
mediaarrayMedia sent: id, locked (paid and not bought yet) and its price in stars
reactionsarrayReactions on a fan message
statusstringFor messages you send: queued, sent, read or failed
atstringISO 8601, UTC

sent_by decides the commission rate of a sale, so it is set by the server and never by your request.

The sale object #

{
  "sale_id": "sale_8812",
  "fan_id": "fan_921",
  "creator_id": "cr_lea",
  "account_id": "acc_1001",
  "conversation_id": "conv_1001_88231",
  "stars": 300,
  "net_cents": 390,
  "sent_by": "ai",
  "kind": "offer"
}
FieldTypeDescription
sale_idstringStarts with sale_
fan_id, creator_id, account_id, conversation_idstringWho bought, from whom, where
starsnumberPrice paid in Telegram Stars
net_centsnumberNet value in US cents (a Star counts $0.013)
sent_bystringai, human or script: who sent the offer, it sets the commission rate
kindstringWhat was bought, for example offer

The same object arrives by webhook, event sale.completed.

Fans #

List fans #

GET /fans · can_fan_read

ParameterTypeRequiredDescription
creator_idstringNoOnly the fans of this creator
statusstringNoA status tag of the Fans page, for example converted
list_idstringNoOnly the fans of this list
qstringNoPart of the fan's name or username
spent_minnumberNoOnly fans who spent at least this many Stars
limit, cursornumber, stringNoPagination
curl "https://api.lanachat.ai/v1/fans?creator_id=cr_lea&spent_min=1000&limit=50" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
{
  "data": [
    { "id": "fan_921", "display_name": "Marco", "username": "marco_dl", "creator_id": "cr_lea", "purchases": 9, "spent": { "stars": 1450, "net_cents": 1885 }, "lists": ["vip"] }
  ],
  "next_cursor": null
}

Get a fan #

GET /fans/{id} · can_fan_read

curl https://api.lanachat.ai/v1/fans/fan_921 \
  -H "Authorization: Bearer $LANACHAT_TOKEN"

The answer is the fan object. An unknown id answers 404.

Update a fan #

PATCH /fans/{id} · can_chat

Send only what changes: the fan's alias, reply language or notes. The answer is the updated fan.

curl -X PATCH https://api.lanachat.ai/v1/fans/fan_921 \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Likes beach sets, buys at night"}'

List a fan's purchases #

GET /fans/{id}/sales · can_sales_read

curl https://api.lanachat.ai/v1/fans/fan_921/sales \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
{
  "data": [
    { "sale_id": "sale_8812", "fan_id": "fan_921", "creator_id": "cr_lea", "account_id": "acc_1001", "conversation_id": "conv_1001_88231", "stars": 300, "net_cents": 390, "sent_by": "ai", "kind": "offer" }
  ],
  "next_cursor": null
}

A paged list of sale objects. For totals per day, link or source, use stats.

Import fan history #

POST /fans/import · can_fan_import

Adds past fans and their messages to your records, for example from another tool. It never sends a message to a fan and never confirms a purchase.

curl -X POST https://api.lanachat.ai/v1/fans/import \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d @fans-history.json

Fan lists #

List fan lists #

GET /fan-lists · can_fan_read

curl https://api.lanachat.ai/v1/fan-lists \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
{
  "data": [ { "id": "vip", "name": "VIP" } ],
  "next_cursor": null
}

Create a list #

POST /fan-lists · can_chat

ParameterTypeRequiredDescription
namestringYesThe name shown on the Fans page
curl -X POST https://api.lanachat.ai/v1/fan-lists \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Win back"}'

The answer is the new list (201).

Add or remove fans #

POST /fan-lists/{id}/members and DELETE /fan-lists/{id}/members · can_chat

ParameterTypeRequiredDescription
fan_idsarray of stringsYesThe fans to add or remove
curl -X POST https://api.lanachat.ai/v1/fan-lists/vip/members \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"fan_ids": ["fan_921"]}'

DELETE takes the same body. An unknown list or fan answers 404.

Conversations #

List conversations #

GET /conversations · can_fan_read

ParameterTypeRequiredDescription
creator_idstringNoOnly this creator
account_idstringNoOnly this Telegram account
statestringNoai, human, script or off
unreadbooleanNotrue: only conversations with unread fan messages
limit, cursornumber, stringNoPagination
curl "https://api.lanachat.ai/v1/conversations?creator_id=cr_lea&unread=true" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
{
  "data": [
    { "id": "conv_1001_88231", "fan_id": "fan_921", "creator_id": "cr_lea", "account_id": "acc_1001", "state": "ai", "status": { "tag": "active", "reason": null }, "unread": 2, "last_message_at": "2026-10-06T14:02:51Z", "spent": { "stars": 1450, "net_cents": 1885 }, "source_link_id": "lnk_3" }
  ],
  "next_cursor": null
}

Get a conversation #

GET /conversations/{id} · can_fan_read

curl https://api.lanachat.ai/v1/conversations/conv_1001_88231 \
  -H "Authorization: Bearer $LANACHAT_TOKEN"

The answer is the conversation object; state says who is replying.

Read messages #

GET /conversations/{id}/messages · can_fan_read

ParameterTypeRequiredDescription
translatestringNoA language code, for example en: each message also gets a translation
limit, cursornumber, stringNoPagination, newest first
curl "https://api.lanachat.ai/v1/conversations/conv_1001_88231/messages?limit=2" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
{
  "data": [
    { "id": "msg_77120", "from": "creator", "sent_by": "ai", "text": "Made this one this morning…", "media": [ { "id": "med_5521", "locked": true, "stars": 300 } ], "at": "2026-10-06T14:02:51Z" },
    { "id": "msg_77119", "from": "fan", "text": "show me more 😍", "reactions": [], "at": "2026-10-06T14:02:10Z" }
  ],
  "next_cursor": "eyJpZCI6Im1zZ183NzExOCJ9"
}

sent_by is ai, human (with member_id) or script.

Send a message #

POST /conversations/{id}/messages · can_chat

ParameterTypeRequiredDescription
textstringYes, or mediaThe text, as the creator
mediaarrayNoVault media: id, and stars to sell it locked at that price
send_atstring or nullNoISO 8601 time to schedule it, up to 7 days ahead; null sends now
curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/messages \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"text": "Only for you, tonight 🔥", "media": [{"id": "med_5522", "stars": 250}], "send_at": null}'
{ "id": "msg_77121", "from": "creator", "sent_by": "human", "token_id": "tok_31", "status": "queued", "at": "2026-10-06T14:03:30Z" }
  • A message sent through the API counts as sent by a person (sent_by: "human", with the token that sent it) and takes the conversation over (state: "human") until you release it.
  • status goes queued, sent, then read; failed carries the Telegram reason (for example a flood wait).
  • send_at schedules the message (up to 7 days ahead).

React to a message #

POST /conversations/{id}/reactions · can_chat

ParameterTypeRequiredDescription
message_idstringYesThe fan's message
emojistringYesThe reaction
onbooleanYestrue adds it, false removes it
curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/reactions \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"message_id": "msg_77119", "emoji": "❤️", "on": true}'

Take over #

POST /conversations/{id}/takeover · can_chat

curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/takeover \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
{ "id": "conv_1001_88231", "state": "human", "taken_by": "tok_31", "taken_at": "2026-10-06T14:04:00Z" }

The AI stops answering this fan until you give the conversation back.

Give back to the AI #

POST /conversations/{id}/release · can_chat

curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/release \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

The answer is the conversation with state: "ai". An off conversation for a safety reason cannot be released (409).

Run a script #

POST /conversations/{id}/script · can_chat

ParameterTypeRequiredDescription
script_idstringYesA script of the same creator, see Scripts
curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/script \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"script_id": "scr_204"}'

While it runs, the conversation's state is script.

Mark as read #

POST /conversations/{id}/read · can_chat

curl -X POST https://api.lanachat.ai/v1/conversations/conv_1001_88231/read \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

The conversation's unread goes back to 0.

Latest news about Lana on Telegram

New releases and beta invites, first in the channel.