Chat with an AI agent
The AI agent you built for voice can also hold a text conversation. Each HTTP request-response cycle represents one exchange between the user and the AI. You send the user’s message as an HTTP request, and the agent’s reply comes back in the response. An ongoing text conversation can be escalated to voice and back to text with no loss of conversation context.
Start by sending two requests to an agent saved in your Space through HTTP, then hold conversations from your server, from a web page, and over SMS text messages, move one onto a phone call and back, and receive the agent’s tool calls and summaries.
Prepare for AI chat
Have these values ready:
- Your Space URL, such as
<YOUR_SPACE>.signalwire.com. - Your Project ID and an API token from the Dashboard’s API credentials page, with the token’s Chat permission enabled.
- An agent: a SWML document with an
aimethod, served at a URL SignalWire can fetch from the public internet. The first run below saves one in your Space. - For text messages, a messaging-capable phone number in your Space.
- For tool calls, summaries, text messages, and the web page gateway, a publicly reachable HTTPS URL for your application.
- To move a conversation onto a phone call, a voice-capable number in your Space.
Anything a page can read, a visitor can read. An API token in page code is a published API token,
so a web page reaches AI chat only through ChatGateway
on your server.
Hold your first AI chat conversation
Hold a short text conversation with an agent saved in your Space, using nothing but HTTP.
Save an agent as a hosted SWML script
Save this document as a SWML Script resource in your Space’s Dashboard, or with the
REST API. Its request URL, ending in /relay-bins/<YOUR_AGENT_ID>, is the
address AI chat fetches your agent from. The same resource also answers voice calls.
The REST response carries the request URL in swml_script.request_url.
Create the conversation
create_conversation opens a conversation with your agent under an ID you choose, and returns the
agent’s opening line.
Send a message
Every turn after that is a chat request with the same conversation ID. The agent’s reply comes
back in the same response.
Read the reply
A working turn returns the agent’s reply in result.response, usually within a few seconds. Check
the HTTP status first. An HTTP 401 Unauthorized means the credentials are wrong or the token lacks
the Chat permission, and an HTTP 400 means the request body wasn’t valid JSON.
Errors from AI chat itself arrive with HTTP 200 and an error object in place of result:
-32001,Conversation not found: the ID doesn’t match a live conversation. Check its spelling, or create the conversation again if it ended or sat idle past its timeout.-32002,Configuration error: SignalWire couldn’t fetch your agent fromconfig_url. Check the agent ID at the end of the URL.
The AI chat REST API reference lists the other error codes.
How an AI chat conversation works
Your agent is the SWML document at config_url. A conversation is a series of turns under an ID you
choose, and IDs are scoped to your project. A turn is one user message and the agent’s reply,
including any tool calls the agent makes along the way.
A conversation ID keeps only letters, digits, _, -, ., and :. Any other character is dropped
without an error, so rider~2 is stored as rider2; use . when you compose IDs. A conversation
ends when you end or delete it, or after it sits idle past its conversation_timeout, 3600 seconds
unless you set one on the request that creates it. An ended conversation can’t be reopened: a later chat
that passes config_url under the same ID starts a new conversation with no memory of the old one.
Hold an AI chat conversation
The AI chat REST API reference documents every method, parameter, and error code.
Hold an AI chat conversation via the Server SDKs
AIChatClient sends the same methods from the Server SDKs and turns errors into
exceptions you can catch, such as ConversationNotFoundError when the conversation has ended or
never existed. They all subclass AIChatError, as listed in the
AIChatClient reference.
Hold an AI chat conversation via REST
Read the conversation back with chat_log, then end it with end_conversation. Ending starts
post-processing, such as the summary sent to your post_prompt_url.
chat_log returns every entry in result.chat_log. end_conversation returns the conversation’s
id with status: "ended", or status: "not_found" when no conversation has that ID.
Hold an AI chat conversation from a web page
ChatGateway runs inside your web application and forwards a page’s messages to AI
chat. Your server holds the API token and the config_url. The page holds only the gateway’s URL
and a publishable key, which can reach this one agent and nothing else, because the gateway injects
config_url itself and never accepts it from the page.
The server below serves the chat page and mounts the gateway at /chat/. Localhost is always
allowed, so it runs locally unconfigured; list your production origin in allowed_origins.
The gateway takes four methods on one endpoint, each a POST with the key as a bearer token. The
first start or chat returns a signed conversation handle in the X-Chat-Handle response header,
and every later request sends it back as handle. See the gateway router reference.
A key copied from your page lets someone talk to your agent, and every turn is billed to your
project. The origin allowlist stops the key working in someone else’s page, but not in a script,
which can leave out the Origin header. The gateway caps new conversations at 60 a minute and turns
at 200 per conversation by default; lower them with max_new_conversations and max_turns.
Hold an AI chat conversation over SMS text messages
Answer text messages to your number with the agent. When a message arrives, SignalWire posts the
inbound message webhook to your server, which sends the text to AI chat and
returns a messaging SWML document whose reply carries the agent’s answer.
Then route your number’s messages to this server. Create a SWML Webhook resource whose
URL is your server’s public /sms URL, then assign it to your number under Inbound Message
Settings, as the webhooks page shows. Over the REST API, create the SWML
webhook with used_for set to messaging, then assign it to the
number with handler set to messaging.
Passing config_url on chat creates the conversation on the first message from a sender and
continues it after that, keyed on the sender’s number. Anyone who can reach /sms can run turns
billed to your project, so in production verify the webhook signature on
each request before acting on it.
Move an AI chat conversation onto a phone call and back
A rider chatting with Ada can take the conversation onto a phone call and back to text without
repeating themselves. The conversation stays open during the call. Your server reads it, places a
call whose agent starts with the chat so far, and when the call ends, writes what was said on the
call back into the conversation as a system message. Only two APIs are involved: AI chat, and the
Calling API through the REST client’s calling.dial.
Create a conversation as in the first run, under the ID
chat-demo-3, and send it a message or two. Then ask your server to move it onto the rider’s phone:
The phone rings, and Ada already knows what was typed. Hang up, and a few seconds later the server
prints Ada’s reply to the call; the next chat on chat-demo-3 picks up from there. If the chat
doesn’t know about the call, SignalWire couldn’t reach /post_prompt. If the conversation
sits idle past its conversation_timeout during the call, it ends, and the call has nowhere to
write back to.
/call places calls on your bill, so only your own backend should reach it. /post_prompt writes
into whichever conversation its query string names, so anyone who reaches it can slip a system
message into a conversation whose ID they know or guess. In production,
verify the webhook signature on each request before acting on it.
A web page can move its conversation into a call in the browser instead, through
HandoffRouter mounted beside the gateway. The page places the call with the
Browser SDK’s dial(), passing the chat handle and a nonce in
userVariables. Your application registers that nonce with register when
SignalWire fetches the call’s configuration, and supplies the capture_leg and end_call callbacks
that record each leg and hang up the call. This path ends the text leg when the call starts, so
returning to text continues under a new ID, <id>.1.
AI chat methods: create, chat, read, summarize, end, and delete
Messages take a role of user or system. A system message steers the agent without appearing
as something the user said.
Receive tool calls and summaries from an AI chat conversation
A chat conversation sends the same webhooks as a voice AI session. Tool calls reach the
web_hook_url on your SWAIG functions, and the summary reaches your post_prompt_url a few seconds
after the conversation ends. Tool calling covers the round trip, and webhooks
covers handling them in general.
Both webhooks identify a chat, so one handler can serve voice and text. SignalWire also appends
conversation_id and conversation_type to the tool webhook’s query string.
The summary arrives in post_prompt_data.raw.
chat_log and the post-prompt call_log include your prompt, system messages, and tool entries
alongside the dialogue. Filter to user and assistant before showing either to anyone. The
gateway’s log already does.