text agent, the text counterpart of a
call. Start it with POST /v1/chats, send messages with
POST /v1/chats/{chat_id}/messages (the reply comes back on that request, as
JSON or a stream), and end it with POST /v1/chats/{chat_id}/end. Prompt, tools
and handoffs behave as they do on a call.
A chat stays open until the agent’s end_call tool or
POST /v1/chats/{chat_id}/end ends it. One the person stops writing in ends 24
hours after their last message (close_reason: "inactivity_timeout"), and only
then gets its analysis and session.completed
webhook. If you know your user has left, end the chat yourself.
Chats also start when a message arrives on Telegram or
WhatsApp. Those chats are driven by the channel: sending a
message to one over the API, or minting a token for it, is refused with 409.
Start a chat
uuid
The text agent that answers (published version unless
agent_version says
otherwise). agent, agent_version, agent_override and agent_team work as
on a call; see per-call configuration. A
non-text agent is refused.string
Your stable id for the person, 1–256 characters. Must not start with a
reserved prefix. It is what links a
person’s chats for memory and history. Omit it
for a single-use key with no past.
object
What the agent knows about the person, read as
{{userdata.field}}. Merged onto
the contact’s record, so it carries into later chats. See userdata.201:
started_at stays null until the first message; nothing runs or is charged
before then.
- Starting a chat ends the contact’s open chat with the same agent
(
close_reason: "replaced"). A contact can hold open chats with different agents. - A workspace with no credit is refused with
402.
Send a message
Response
200, the whole turn:
The request waits up to 120 seconds. A
running turn finishes in the
background; read the reply later from GET /v1/chats/{chat_id}/items. A message
to an ended chat is refused with 409.
The latest message wins. If message B arrives while A is still being
answered, A’s request returns status: "canceled" with superseded_by_item_id
set to B, and B’s request returns the reply. A superseded message is not charged.
Stream the reply
With"stream": true the same request answers as server-sent events:
Each frame carries its name in an
event field. The stream stays open until the
turn ends, with a keep-alive comment every 15 seconds. turn.result is read from
the database, so it is authoritative even if a delta was dropped. A refused
request (ended chat, a file over 20 MB) is a normal HTTP error, not a stream. Frame
shapes: streaming.
End a chat
202 with the chat, marked ended at once (the next message is a 409).
Ending an ended chat is a no-op. Then, in the background:
- The agent’s
on_exithook runs; anything it says is saved to the chat’s items. - Analysis runs (on by default for text agents)
and fills
analysis. - The chat is priced and
session.completedis sent. See webhook events.
From a browser
Never put a personal access token in browser code. Your server starts the chat, mints a chat token for it, and passes the response to the page:talqing.chats.tokens.create(...).) The token lasts one hour, is pinned
to one chat, and is refused with 409 for an ended chat. As a Bearer token, from
any origin, it opens exactly four routes for that chat: GET /v1/chats/{chat_id},
GET /v1/chats/{chat_id}/items, POST /v1/chats/{chat_id}/messages and
POST /v1/chats/{chat_id}/end.
A token holder is treated as the customer: items are only the customer_visible
ones (no tool calls or handoffs), and the chat reads back with cost: null,
analysis: null and userdata: {}.
TalqingChat from @talqing/client wraps those routes. It streams every send and
calls fetchToken again shortly before expiry, so have your endpoint mint a token
for the same chat:
chat.items() reloads the history after a page refresh; chat.end() ends the
chat. A browser cannot start a chat or choose a contact_key. Full client
reference: React SDK.
Files
Attach up to four files per message, of any type, as base64data: URLs of
up to 20 MB each:
data: mime. What the agent does
with each file:
Every file is stored either way: text and files arrive as one item and one turn,
and come back on the item’s
attachments, each with a signed URL and
url_expires_at. A file is refused with 400 only when it cannot be stored: empty,
over 20 MB, or not a valid data URL. Limits: documents.
In a browser, talqingFileDataUrl(file) from @talqing/client checks a File,
downscales a photo and encodes it into the data_url.
Voice notes
A text agent hears voice notes only when you give it a speech-to-text model:config.stt, off (null) by default. Each note is transcribed in one request,
on your own provider key, before the agent replies, so only models with
file_transcription set in GET /v1/catalog qualify: today every xAI, Sarvam, Raya, OpenAI gpt-transcribe, ElevenLabs scribe_v2, Deepgram Nova-3 and OpenRouter model. config.language
applies to it as on a call; Auto detects.
status: "transcribed", transcript,
duration_seconds), so every later agent in the conversation reads it too. A note
that arrived while the agent had no stt stays pending and is transcribed on the
next turn of an agent that has one. A file that cannot be decoded, or that the
provider refuses, is failed with a failed_reason. Transcription is billed per
second of audio at the model’s rate, as a line on the chat’s cost.
Read chats back
Re-running or previewing analysis on an ended chat: call analysis.
A chat is a session of its conversation, so it also appears under
GET /v1/conversations/{conversation_id}/sessions beside calls. See
conversations.
Items
Items also carry
session_id, agent_id, agent_version, attachments and
per-turn metrics.
Hooks, handoffs and memory
on_enterruns once, on the first message;on_exitruns once, when the chat ends.- A handoff lasts for the rest of the chat: the new agent answers every later
message. Within a chat the agent sees the whole chat so far, or what the
handoff’s
contextpassed it plus everything since. See handoffs. - What a new chat knows of earlier ones is the agent’s
conversation.context(none,summary,transcript), as on calls. See conversation memory.
Cost and retention
- $0.0001 per answered message (turn finished
done) is the platform fee on text, drawn from credits.canceledanderrorturns are free. Model spend goes to your provider key. See pricing and credits. costfills in while the chat is open and is final once it ends.- Credit is checked at start (
402) and again when a chat resumes after sitting idle. Out of credit then, the turn returnsstatus: "error"and the agent does not run. - Under a retention policy, content is
deleted
retention_daysafter the chat ends. Open chats are never purged.
Next
Website chat guide
A text agent in your own React app, end to end.
Conversations
Sessions, items and a person’s whole history.