Chat API
The Chat API runs a text conversation with an agent over plain REST — same brain, knowledge bases (RAG) and tools as a voice call, no WebSocket, no audio. Build your own chat UI or bot on top of an agent without running your own RAG pipeline.
All endpoints live under /api/v1 and authenticate with the X-API-Key header (call them from your server, never the browser) — see Authentication. Responses are flat JSON (no {success,data} envelope). Conversations appear in Call history as chat with the full transcript, billing and post-call analysis.
For a step-by-step build, see Guide: Build a chat bot. To embed a ready-made chat widget instead of building your own UI, enable Text chat in the agent's Publish panel.
Using the SDKs? The Node & Python backend SDKs wrap these endpoints —
tn.chat.send(...)/tn.chat(...), plus a built-in send-loop helper (chatLoop/chat_conversation) that handles the410restart and409retry for you. The raw REST below is the same thing without the SDK.
Send a message
POST /api/v1/chat
First message — creates the session
Omit sessionId; the response returns one. identifier is your unique id for the end user (1–128 chars) — it binds the session to that user so a leaked sessionId can't be reused by someone else.
curl -X POST https://api.telenow.ai/api/v1/chat \
-H "X-API-Key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{ "agentId": "…", "identifier": "user-42", "input": "Hello!" }'
{ "sessionId": "…", "reply": "Hi! How can I help?", "turn": 1, "identifier": "user-42" }
Subsequent messages
Send the returned sessionId (with the same identifier); the agent keeps the full conversation context.
curl -X POST https://api.telenow.ai/api/v1/chat \
-H "X-API-Key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{ "agentId": "…", "identifier": "user-42", "sessionId": "…", "input": "Tell me more" }'
Request fields
| Field | Type | Notes |
|---|---|---|
agentId | UUID | Required. Must belong to your org and be active |
identifier | string | Required. Your unique end‑user id, 1–128 chars |
input | string | Required. The user's message |
sessionId | UUID | Omit on the first message; send the returned id afterwards |
variables | object | First message only — { "name": "value" } map for the agent's context variables. Missing required variables → 400 listing their names |
optionId | string | Optional. The user tapped one of the options the previous reply's ui offered: send that option's id here and its title as input. The agent is told which option it was. An id the session did not offer is ignored |
Response
{ "sessionId": "…", "reply": "…", "turn": 2, "identifier": "user-42" }
turn counts the user messages the session has seen (1‑based).
A reply can also carry a ui field: options for the user to tap, one link button, or a file. See Options and link buttons and Files from your library.
Options and link buttons
When the agent's owner turns on Buttons and links in chat replies (agent → Call handling), the agent may attach options the user taps, or one link button, to a reply. The response then has a ui field. It is absent otherwise, so nothing changes for agents that do not use it.
{
"sessionId": "…",
"reply": "Which plan would you like?",
"turn": 3,
"identifier": "user-42",
"ui": {
"kind": "options",
"shownAs": "buttons",
"menuLabel": "Plans",
"options": [
{ "id": "plan_basic", "title": "Basic" },
{ "id": "plan_pro", "title": "Pro", "description": "Billed monthly" }
]
}
}
| Field | Notes |
|---|---|
kind | options, link or file (files). More kinds may be added later: ignore one you do not know, since reply reads on its own |
shownAs | How to draw it: buttons for a few short choices, list for more or with descriptions, link_button for a link. text means the options are already in reply as numbered lines (a reply with no words of its own), so draw nothing more |
options | kind: options — the choices in order, each { id, title, description? } |
menuLabel | kind: options — the agent's label for a button that opens them as a menu, when it gave one |
label, url | kind: link — the button's words and an https link |
Draw the options under the reply, and do not repeat them in the text. When the user taps one, send its title as input and its id as optionId:
curl -X POST https://api.telenow.ai/api/v1/chat \
-H "X-API-Key: vai_live_…" \
-H "Content-Type: application/json" \
-d '{ "agentId": "…", "identifier": "user-42", "sessionId": "…", "input": "Pro", "optionId": "plan_pro" }'
A link the agent attaches is always https, and always one that appeared in the agent's own instructions, its knowledge base or a tool result in this conversation; the agent cannot invent one, and a link the user sent is never used. The transcript below returns the same ui on the reply that carried it.
Files from your library
An agent can also send one of your organisation's own files (a menu, a price list, a form) with a reply. Owners and admins add files to the library and choose, per agent, which ones it may send (agent → Call handling → Files this agent may send). The library takes JPEG and PNG images and PDF, Word, Excel, PowerPoint and text documents. An agent that has none chosen sends none.
The reply then has a ui of kind: file:
{
"reply": "Here is our menu.",
"ui": {
"kind": "file",
"shownAs": "file",
"id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
"name": "Menu.pdf",
"mime": "application/pdf",
"sizeBytes": 240512,
"url": "https://api.telenow.ai/api/chat-files/…",
"urlExpiresAt": "2026-11-06T10:00:00Z"
}
}
| Field | Notes |
|---|---|
id, name, mime, sizeBytes | The library file, as the user sees it |
url | A signed link to this one file, with no sign-in. It works until urlExpiresAt (a day by default); every response and every read of the transcript gives a fresh one. A file deleted from the library stops opening |
Show an image as a picture and anything else as a download. The agent only ever sends a file its owner chose for it, from its own organisation's library.
Get the transcript
GET /api/v1/chat/:sessionId/messages
Returns the full user/assistant transcript (system messages are omitted). Scoped to your org by the key — a session that belongs to another org returns 404. An agent reply that carried options, a link button or a file has the same ui field as the turn's response (a file's url is a fresh one).
{
"sessionId": "…",
"messages": [
{ "role": "user", "content": "Hello!", "createdAt": "2026-06-13T10:00:01Z" },
{ "role": "assistant", "content": "Hi! How can I help?", "createdAt": "2026-06-13T10:00:03Z" }
]
}
End a conversation
POST /api/v1/chat/:sessionId/end
Ends the session through the same teardown as a widget close — duration, billing settlement and post‑call analysis all run. Idempotent: ending an already‑ended session still returns 200. Only chat-API sessions can be ended here; pointing it at a voice session returns 404.
{ "sessionId": "…", "ended": true }
Sessions you never end explicitly are closed automatically after 30 minutes of inactivity (or at the agent's configured max duration, if set). Calling end promptly is good hygiene — it settles billing and triggers analysis immediately rather than waiting for the idle timeout.
Errors
Errors return { "error": "…" } with the status code:
| Status | When | What to do |
|---|---|---|
400 | Missing/invalid field, inactive agent, missing required variables (names listed) | Fix the request |
401 | Missing or invalid API key | Check X-API-Key |
403 | identifier doesn't match the session's bound end user | Use the identifier the session was created with |
404 | Unknown agent/session, or it belongs to another org | Check the ids |
409 | { "error": "turn in progress" } — a reply is still being generated for this session | Wait for the in‑flight reply, then retry |
410 | { "error": "session expired", "code": "SESSION_EXPIRED" } — the session ended, idled out, or the server restarted | Restart: resend the message without sessionId to start a fresh session, then continue with the new id |
Notes
- Replies are synchronous — the HTTP response returns when the agent's full reply (including any tool calls) is ready. Use a generous client timeout (60 s+).
- One turn at a time per session: serialize your sends per
sessionIdand handle409by waiting. - The agent's HTTP tools, knowledge bases and context variables all work exactly as on voice calls; voice‑only native tools (transfer, hangup) are unavailable in chat.
- Chat sessions fire the same webhook events as calls —
call.startedwhen the session is created,tool.invokedper tool call, andcall.ended+call.analyzedat teardown — so you can ingest chat outcomes alongside voice.
Related
- Guide: Build a chat bot — end-to-end recipe with a loop and error handling.
- Backend SDKs → Text chat — the Node/Python wrappers (
tn.chat,chatLoop/chat_conversation). - Publishing & embedding — the ready-made chat widget if you don't want a custom UI.
- Authentication — how
X-API-Keyworks and which key roles are allowed. - Context variables · Knowledge bases · Tools.