Chat — client.chat
:::info Requires bessai ≥ 0.2.0
The chat namespace ships in SDK version 0.2.0 — pip install --upgrade bessai
(check with bessai.__version__).
:::
Deploy the same agents you use for voice — prompt, LLM, tools, skills, knowledge bases, MCP — as
text chat, over a synchronous REST transport or a realtime LiveKit text room. Chat sessions
are a separate resource from calls (no CallResponse/calls row is involved).
create(**params) → ChatSession
POST /v1/chat/sessions — create a chat session for a published agent.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | str | yes | — | UUID of the published agent. |
transport | str | no | "rest" | "rest" (reply via send_message()) or "livekit" (text-only room). |
channel_type | str | no | "api" | "api", "web_widget", or "dashboard_test". |
dynamic_variables | dict[str,str] | no | None | Values injected into the system prompt. |
external_user_key | str | no | None | Your end-user's stable id — threads their sessions together. |
transport="rest" sessions carry the agent's opening line in session.greeting (None if the
agent has no greeting configured) — reply via send_message(). transport="livekit" sessions
return session.access_token / session.room_url / session.room_name for a text-only LiveKit
room instead — connect with the LiveKit JS/Web SDK and exchange text over the lk.chat topic;
greeting is None there (the chat worker sends it over the room).
session = client.chat.create(agent_id="ag_abc123", external_user_key="visitor-42")
print(session.greeting.content) # "Hi! How can I help you today?"
create_test_session(**params) → ChatSession
POST /v1/chat/test — mirror of call.create_test_call(): accepts draft agents and a
temp_config dict of non-persisted overrides. REST transport only — the fastest loop for
iterating on a prompt. Params: agent_id, temp_config, dynamic_variables.
send_message(session_id, content) → ChatTurnResult
POST /v1/chat/sessions/{id}/messages — send a user message on a REST-transport session and get
the assistant's reply (synchronous JSON). Raises on livekit-transport sessions (409) — those
talk over the room's lk.chat text stream instead. content is 1–4,000 characters.
ChatTurnResult fields: session_id, assistant_content, tool_calls (list of
{"name","arguments","output"}), tokens_in, tokens_out, session_closed (bool — true if this
turn closed the session), close_reason (agent_end_chat | message_cap |
insufficient_credits | explicit_close | None), user_message_id, assistant_message_id.
reply = client.chat.send_message(session.session_id, "Do you have a table for 4 tonight?")
print(reply.assistant_content, reply.session_closed)
retrieve(session_id, skip=0, limit=50) → ChatSessionDetail
GET /v1/chat/sessions/{id} — session detail plus the paginated message thread (oldest first).
Extends ChatSession with dynamic_variables, session_metadata, and messages
(list[ChatMessage]).
list(skip=0, limit=20, agent_id=None, status=None, channel_type=None, from_date=None, to_date=None) → list[ChatSession]
GET /v1/chat/sessions — your org's sessions, newest first. status ∈ active|idle|closed;
channel_type ∈ api|web_widget|dashboard_test.
close(session_id) → ChatSessionCloseResult
POST /v1/chat/sessions/{id}/close — explicitly close a session; queues the post-chat pipeline
(summary/sentiment/automation). Raises if already closed.
ChatSession fields: session_id, organization_id, agent_id, agent_version,
channel_type, widget_id, external_user_key, status (active→idle→closed),
livekit_room_name, message_count, agent_reply_count, total_tokens_in, total_tokens_out,
summary, sentiment, session_cost, processing_status, started_at, last_message_at,
closed_at, created_at, plus greeting/access_token/room_url/room_name on the create()
response only. ChatMessage fields: message_id, session_id, role
(user|assistant|tool|system), content, tool_calls, tokens_in, tokens_out,
created_at.
Full turn loop (Python):
session = client.chat.create(agent_id="ag_abc123", external_user_key="visitor-42")
if session.greeting:
print("agent:", session.greeting.content)
for user_msg in ["Hi, do you have a table for 4?", "Tonight at 8pm works great."]:
print("user:", user_msg)
reply = client.chat.send_message(session.session_id, user_msg)
print("agent:", reply.assistant_content)
if reply.session_closed:
break
client.chat.close(session.session_id)
Cost & limits: chat is billed per agent reply (use agent_reply_count /
total_tokens_in/total_tokens_out to reconcile); input messages are capped at 4,000 characters;
output is capped by the agent's max_completion_tokens; each turn sends the last 30 messages of
history to the LLM. See the Partner API Guide's rate/cost notes
for the full picture, including the credit-sufficiency gate.