ai_sidecar
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.
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:
idle_timeout_ms), or the agent starting to speak.insight), or calls the built-in sidecar_skip tool to stay
silent when no advice is needed.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.
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.
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.
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.
The call legs to observe. Possible values: remote-caller, local-caller.
Both legs are required; defaults to both legs.
Which leg is the customer, used as the turn-end trigger source.
Possible values: remote-caller, local-caller.
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.
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.
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.
Whether SWAIG tools may run SWML on the call.
Whether SWAIG tools may change the sidecar’s settings, such as the model.
Whether SWAIG tools may set the sidecar’s 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}.
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"].
An object containing tuning options for the sidecar.
See params for more details.
As it observes the call, the sidecar reports each step of its activity as a structured callback on two paths:
calling.ai.sidecar — always fires. Subscribe with the SignalWire RELAY or browser SDK to consume callbacks in real time.url is set, as an HTTP POST to that URL with the callback body wrapped under sidecar_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.
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).
Each callback carries a type field identifying what occurred, along with type-specific fields.
The final callback carries a stop_reason indicating why the sidecar stopped.
The error callback carries an error_reason describing the failure.
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.
The platform sends the following fields in the POST body:
The name of the function the model is calling.
The arguments the model passed. May arrive as a JSON-encoded string (as shown below) or as a parsed object under an arguments key — accept either.
The sidecar’s current global_data.
Call identifiers: call_id, caller_id_name, caller_id_number, and destination_number.
Your SignalWire project ID.
Your SignalWire space ID.
Your webhook must return a JSON object with the result and any actions to take:
The result the model sees. It becomes the tool result the model reads on its next step.
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.
Each entry in the action array is an object keyed by the action name. The sidecar supports the following:
Emits a user_event carrying your topic and any payload fields — the primary way to surface UI alerts and intent navigation to your application.
A key-value object merged into global_data. Emits a global_data_change callback and persists across sessions on the same call leg.
The key to remove from global_data, or a new object to replace it.
A key-value object of session metadata.
The metadata key to remove, or a new object to replace it.
Transfers the call to a destination. Ends the sidecar and transcription.
The destination — a phone number, SIP URI, or SWML URL.
Whether to include a conversation summary when transferring.
When true, hangs up the call. Ends the sidecar and transcription.
When true, stops the sidecar only; transcription continues.
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".
Enables or disables functions mid-conversation. Each entry sets a function’s active state.
The name of the function to toggle.
Whether to enable or disable the function.
Changes the sidecar’s model settings, such as the model or temperature. Gated by swaig_allow_settings.
A SWML document to run on the call. Gated by swaig_allow_swml. The transfer: true variant ends the sidecar.
Injects a message into the conversation and triggers a new evaluation — use it to send the sidecar a question or instruction mid-call.
Controls whether the model may chain tool calls — true, or "forever" for unlimited chaining.
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.
sidecar_skip toolThe 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.
ai_sidecar and plain live_transcribe cannot run on the same call at the same time — starting one while the other is active is rejected.direction (for example, only remote-caller) is rejected.deepgram is the default speech engine, and turn-end detection is calibrated for it.say doesn’t speak. The sidecar has no voice on the call, so a say action is reported as a callback instead of being spoken aloud.final callback fires and it stops.sidecar_skip is reserved. Do not define a function with that name.error callback with error_reason: tool_loop. Make sure your tools return clear, useful results and your prompt tells the model what to do after a tool returns.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.