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 the 410 restart and 409 retry 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

FieldTypeNotes
agentIdUUIDRequired. Must belong to your org and be active
identifierstringRequired. Your unique end‑user id, 1–128 chars
inputstringRequired. The user's message
sessionIdUUIDOmit on the first message; send the returned id afterwards
variablesobjectFirst message only — { "name": "value" } map for the agent's context variables. Missing required variables → 400 listing their names
optionIdstringOptional. 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.

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" }
    ]
  }
}
FieldNotes
kindoptions, link or file (files). More kinds may be added later: ignore one you do not know, since reply reads on its own
shownAsHow 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
optionskind: options — the choices in order, each { id, title, description? }
menuLabelkind: options — the agent's label for a button that opens them as a menu, when it gave one
label, urlkind: 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"
  }
}
FieldNotes
id, name, mime, sizeBytesThe library file, as the user sees it
urlA 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:

StatusWhenWhat to do
400Missing/invalid field, inactive agent, missing required variables (names listed)Fix the request
401Missing or invalid API keyCheck X-API-Key
403identifier doesn't match the session's bound end userUse the identifier the session was created with
404Unknown agent/session, or it belongs to another orgCheck the ids
409{ "error": "turn in progress" } — a reply is still being generated for this sessionWait for the in‑flight reply, then retry
410{ "error": "session expired", "code": "SESSION_EXPIRED" } — the session ended, idled out, or the server restartedRestart: 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 sessionId and handle 409 by 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.started when the session is created, tool.invoked per tool call, and call.ended + call.analyzed at teardown — so you can ingest chat outcomes alongside voice.