ai_sidecar
ai_sidecar
Attaches a real-time AI observer to a live call. The sidecar listens as a third party and never speaks on the call — after each customer turn, it sends agent-facing advice to your application as webhook callbacks that an agent’s UI (or any consumer) can render. Think of it as a coach watching over the agent’s shoulder: it runs alongside the call rather than driving it.
Use it for live sales coaching, real-time compliance flagging, intent-based UI navigation, voice-of-customer signal
extraction, and supervisor-on-shoulder workflows. A common pattern is to attach ai_sidecar to coach a human agent on a
connect-bridged call: the customer talks to the human, and the sidecar coaches
the human’s screen.
How it works
The sidecar listens to the call but never speaks on it. A call can run either the sidecar or plain live transcription, but not both at once.
Each time the customer finishes speaking, the sidecar evaluates the conversation. This evaluation is called a tick, and
every callback the sidecar produces carries a tick_id. On each tick:
- The sidecar detects the end of the customer’s turn — a final transcription result followed by a brief silence
(
idle_timeout_ms), or the agent starting to speak. - It sends the running transcript to the model, along with your operator prompt and your SWAIG tools.
- The model returns a single line of agent-facing advice (an
insight), or calls the built-insidecar_skiptool to stay silent when no advice is needed. - Any tools the model calls (lookups, alerts, intent triggers) run through your SWAIG functions and MCP servers.
- Every step is reported as a structured callback your application can consume. See Webhook callbacks below for the full catalog.
Properties
ai_sidecar
An object that attaches a real-time AI observer to the call. Accepts the following properties to configure the sidecar’s prompt, language, model, tools, permissions, and other settings.
ai_sidecar.prompt
The operator prompt that instructs the sidecar how to coach the agent. May be a plain string, a Prompt Object Model (POM), or a server-side file reference. SignalWire automatically adds built-in instructions for the sidecar’s role, so your prompt only needs to describe the coaching behavior.
Optional — when omitted, the sidecar falls back to a minimal default prompt, so setting one is strongly recommended. See prompt for the supported forms.
ai_sidecar.lang
The conversation language as a single BCP-47 tag (e.g. en-US).
Sets the speech-recognition language, and is shared with the model as a hint.
ai_sidecar.model
The model used for the sidecar’s advice and its end-of-call summaries.
Suggested values: gpt-4o-mini, gpt-4.1-mini, gpt-4.1-nano.
ai_sidecar.direction
The call legs to observe. Possible values: remote-caller, local-caller.
Both legs are required; defaults to both legs.
ai_sidecar.customer_role
Which leg is the customer, used as the turn-end trigger source.
Possible values: remote-caller, local-caller.
ai_sidecar.url
The webhook URL the sidecar POSTs its callbacks to — both transcription events and sidecar callbacks.
When unset, callbacks are published only on the relay topic (calling.ai.sidecar) and no webhook POST is made — the relay event always fires, the webhook is opt-in.
Basic auth can be embedded in the URL in the format username:password@url.
See Webhook callbacks below for the callback payload shape.
ai_sidecar.SWAIG
SWAIG functions and MCP servers available to the sidecar.
See SWAIG for more details.
The function name sidecar_skip is reserved and auto-registered as a built-in — do not declare it.
ai_sidecar.permissions
SWAIG permission overrides. Defaults to all permissions enabled.
Setting act_on_channel to false
overrides all of these — actions are reported as callbacks but never applied to the call.
permissions.swaig_allow_swml
Whether SWAIG tools may run SWML on the call.
permissions.swaig_allow_settings
Whether SWAIG tools may change the sidecar’s settings, such as the model.
permissions.swaig_set_global_data
Whether SWAIG tools may set the sidecar’s global data.
ai_sidecar.global_data
A key-value object for the initial global_data. You can reference it in the prompt with variable expansion
(e.g. ${global_data.property_name}), and it is included in the requests sent to your tools.
It also persists across sessions on the same call leg through the ai_agents_global_data SWML variable, so later verbs
on the same leg can read it as ${ai_agents_global_data.property_name}.
ai_sidecar.hints
Speech-recognition hints passed to ASR to bias recognition toward specific terms — product names, competitor names,
jargon, customer names, or anything in your SWAIG enum lists. Strongly recommended.
Example: ["ACME", "Globex", "FedRAMP", "SOC 2"].
ai_sidecar.params
An object containing tuning options for the sidecar.
See params for more details.
ai_sidecar.action
Summarize the conversation instead of starting a sidecar. When you include action.summarize, the request
generates a one-off summary and returns rather than attaching a sidecar.
action.summarize
Generate a one-off summary of the conversation and send it to a webhook.
summarize.webhook
The webhook URL the summary is sent to. Defaults to the sidecar’s configured url.
summarize.prompt
The prompt used to write the summary. Defaults to the configured ai_summary_prompt.
Webhook callbacks
As it observes the call, the sidecar reports each step of its activity as a structured callback on two paths:
- Relay topic
calling.ai.sidecar— always fires. Subscribe with the SignalWire RELAY or browser SDK to consume callbacks in real time. - Webhook — fires only when
urlis set, as an HTTPPOSTto that URL with the callback body wrapped undersidecar_event.
Both paths carry the same payload, and each callback fires exactly once on the relay topic regardless of whether a webhook is configured — the relay always fires, the webhook is opt-in.
Callback body shape
Each callback is POSTed to your url with the event wrapped under sidecar_event, alongside a call_info envelope describing the call. project_id and space_id are included when available:
The actual event is under sidecar_event — unwrap that in your code before reading type and the event’s fields. Every callback also carries type, ts (microsecond timestamp), tick_id, and a channel_data object (call_id, plus caller_id_name / caller_id_number / destination_number when available).
See the AI sidecar callback webhook page for the full payload reference. The per-type fields are listed below.
Callback types
Each callback carries a type field identifying what occurred, along with type-specific fields.
On the callbacks that come from a single evaluation (request, thought, tool_call, tool_result, insight),
iter counts the steps within that evaluation, starting at 0, and total_iters on the insight is how many steps it
took in total. Any callback produced while answering an ai_sidecar.ask also
carries that ask’s ask_id and triggered_by: "ask", so you can match it to the question you sent.
Stop reasons
The final callback carries a stop_reason indicating why the sidecar stopped.
Error reasons
The error callback carries an error_reason describing the failure.
Tool webhook contract
When the model calls one of your SWAIG functions, the platform sends an HTTP POST to that function’s web_hook_url. Your webhook runs the function and returns the result. Both the request and the response are plain JSON.
Request
The platform sends the POST body shown below. The sidecar sends a smaller set of fields than the
regular ai method’s
SWAIG webhook, and it groups the
caller details under channel_data.
See the AI sidecar SWAIG tool webhook webhook page for the full field reference.
Response
Your webhook must return a JSON object with the result and any actions to take:
response
The result the model sees. It becomes the tool result the model reads on its next step.
action
Optional. One action object, or an array of them, for the sidecar to perform. Each is keyed by an action name — see Supported SWAIG actions. When act_on_channel is true, the actions take effect on the call; otherwise they are reported as callbacks only.
Supported SWAIG actions
Each entry in the action array is an object keyed by the action name. The sidecar supports the following:
action[].user_event
Emits a user_event carrying your topic and any payload fields — the primary way to surface UI alerts and intent navigation to your application.
action[].set_global_data
A key-value object merged into global_data. Emits a global_data_change callback and persists across sessions on the same call leg.
action[].unset_global_data
The key to remove from global_data, or a new object to replace it.
action[].set_meta_data
A key-value object of session metadata.
action[].unset_meta_data
The metadata key to remove, or a new object to replace it.
action[].transfer
Transfers the call to a destination. Ends the sidecar and transcription.
transfer.dest
The destination — a phone number, SIP URI, or SWML URL.
transfer.summarize
Whether to include a conversation summary when transferring.
action[].hangup
When true, hangs up the call. Ends the sidecar and transcription.
action[].stop
When true, stops the sidecar only; transcription continues.
action[].say
Report-only in sidecar mode. The sidecar has no voice, so instead of speaking it fires an action callback with spoken: false and reason: "no_tts_in_sidecar".
action[].toggle_functions
Enables or disables functions mid-conversation. Each entry sets a function’s active state.
toggle_functions[].function
The name of the function to toggle.
toggle_functions[].active
Whether to enable or disable the function.
action[].settings
Changes the sidecar’s model settings, such as the model or temperature. Gated by swaig_allow_settings.
action[].SWML
A SWML document to run on the call. Gated by swaig_allow_swml. The transfer: true variant ends the sidecar.
action[].user_input
Injects a message into the conversation and triggers a new evaluation — use it to send the sidecar a question or instruction mid-call.
action[].back_to_back_functions
Controls whether the model may chain tool calls — true, or "forever" for unlimited chaining.
action[].extensive_data
Controls how much data is included in the webhook payload.
Setting act_on_channel to false makes all of these report-only — they still fire as callbacks but are not applied to the call.
Built-in sidecar_skip tool
The sidecar provides one built-in tool, sidecar_skip, that you do not declare. The model calls it when the latest customer turn needs no advice.
The tick then ends silently — no insight callback fires — and a skip callback is emitted with the model’s reason.
Instruct the model in your prompt to call sidecar_skip when there is nothing useful to say — otherwise it will tend to fill silence with low-quality advice.
The name sidecar_skip is reserved; do not define a function with that name.
Limitations
- One transcriber per call.
ai_sidecarand plainlive_transcribecannot run on the same call at the same time — starting one while the other is active is rejected. - Both legs required. A single-leg
direction(for example, onlyremote-caller) is rejected. - Turn detection is tuned for Deepgram.
deepgramis the default speech engine, and turn-end detection is calibrated for it. saydoesn’t speak. The sidecar has no voice on the call, so asayaction is reported as a callback instead of being spoken aloud.- The sidecar stops with the call. When the call hangs up or transcription stops, the sidecar’s
finalcallback fires and it stops. sidecar_skipis reserved. Do not define a function with that name.- Tool loops are capped. If the model calls tools in a runaway loop — too many calls in one tick, the same call repeated, or a tool that isn’t registered — the sidecar stops calling tools for that tick and returns an
errorcallback witherror_reason: tool_loop. Make sure your tools return clear, useful results and your prompt tells the model what to do after a tool returns.
Example
Answer the call, attach the sidecar with a prompt, language, webhook URL, hints, and a single SWAIG function,
then bridge the call with connect and hang up.