For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Log inSign up
Support
ReferenceGuides
ReferenceGuides
  • Core
    • Introduction to SWML
    • Expressions
    • Template functions
    • Variables
    • Errors
  • Calling
    • Overview
    • ai
    • ai_sidecar
      • params
      • prompt
      • SWAIG
    • amazon_bedrock
    • answer
    • cond
    • connect
    • denoise
    • detect_machine
    • enter_queue
    • execute
    • goto
    • hangup
    • join_conference
    • join_room
    • label
    • live_transcribe
    • live_translate
    • pay
    • play
    • prompt
    • receive_fax
    • record
    • record_call
    • request
    • return
    • send_digits
    • send_fax
    • send_sms
    • set
    • sip_refer
    • sleep
    • stop_denoise
    • stop_record_call
    • stop_tap
    • switch
    • tap
    • transcribe
    • transcribe_stop
    • transfer
    • unset
    • user_event
  • Messaging
    • Overview
    • execute
    • goto
    • label
    • receive
    • reply
    • request
    • return
    • switch
    • transfer
LogoLogoSignalWire Docs
Log inSign up
Support
On this page
  • How it works
  • Properties
  • Webhook callbacks
  • Callback body shape
  • Callback types
  • Stop reasons
  • Error reasons
  • Tool webhook contract
  • Request
  • Response
  • Supported SWAIG actions
  • Built-in sidecar_skip tool
  • Limitations
  • Example
Calling

ai_sidecar

|View as Markdown|Open in Claude|
Was this page helpful?
Edit this page
Previous

params

Next
Built with

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:

  1. 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.
  2. It sends the running transcript to the model, along with your operator prompt and your SWAIG tools.
  3. The model returns a single line of agent-facing advice (an insight), or calls the built-in sidecar_skip tool to stay silent when no advice is needed.
  4. Any tools the model calls (lookups, alerts, intent triggers) run through your SWAIG functions and MCP servers.
  5. Every step is reported as a structured callback your application can consume. See Webhook callbacks below for the full catalog.

Properties

ai_sidecar
objectRequired

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
string | object

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
stringRequired

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
stringDefaults to gpt-4o-mini

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
string[]

The call legs to observe. Possible values: remote-caller, local-caller. Both legs are required; defaults to both legs.

ai_sidecar.customer_role
stringDefaults to remote-caller

Which leg is the customer, used as the turn-end trigger source. Possible values: remote-caller, local-caller.

ai_sidecar.url
string

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
object

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
object

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
booleanDefaults to true

Whether SWAIG tools may run SWML on the call.

permissions.swaig_allow_settings
booleanDefaults to true

Whether SWAIG tools may change the sidecar’s settings, such as the model.

permissions.swaig_set_global_data
booleanDefaults to true

Whether SWAIG tools may set the sidecar’s global data.

ai_sidecar.global_data
object

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
string[]

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
object

An object containing tuning options for the sidecar.

See params for more details.

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 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.

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:

JSON
1{
2 "call_info": {
3 "project_id": "...",
4 "space_id": "...",
5 "call_id": "...",
6 "content_type": "text/json",
7 "content_disposition": "post_data",
8 "conversation_type": "voice"
9 },
10 "sidecar_event": {
11 "type": "insight",
12 "ts": 1745870400123456,
13 "tick_id": 7,
14 "channel_data": { },
15 "raw": "Confirm the customer's address.",
16 "iter": 0,
17 "total_iters": 1
18 }
19}

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).

Callback types

Each callback carries a type field identifying what occurred, along with type-specific fields.

typeWhenKey fields
startThe sidecar attached to the callmodel, tools (the tool names available), global_data
turnThe customer finished a turn (an evaluation is about to run)transcript_delta, customer_text, agent_text
requestThe sidecar called the modelmodel
thoughtThe model produced text while still working, for example alongside a tool calltext
insightThe sidecar’s advice for the agentraw (the advice text)
skipThe model called sidecar_skip to stay silent for this turnreason?
tool_callThe model called one of your toolsname, arguments?
tool_resultOne of your tools returned a resultname, response
actionA SWAIG action was returned (and, if enabled, executed)action, source_function?, executed
global_data_changeA set_global_data / unset_global_data action changed global_datakey, old_value?, new_value?
history_prunedThe conversation history was trimmed to fit the token budgetdropped_count
errorSomething failed, or an anti-loop guard trippederror_reason, detail?
stopThe sidecar is shutting downstop_reason
finalThe last callback before the sidecar stops — a full snapshot of the sessionstop_reason, summary?, history, transcript?, event_log, stats (counts such as tool calls and insights), metrics, global_data, model, started_at, ended_at, duration_ms

Stop reasons

The final callback carries a stop_reason indicating why the sidecar stopped.

stop_reasonTrigger
transcribe_closeThe call ended normally
transferredA SWAIG transfer action terminated the call
hung_upA SWAIG hangup action terminated the call
stop_actionA SWAIG stop action stopped the sidecar (transcription continues)
api_stopThe sidecar was stopped programmatically

Error reasons

The error callback carries an error_reason describing the failure.

error_reasonTrigger
tool_loopAn anti-loop guard tripped (more than four tool calls in a tick, or the same tool was called with the same arguments repeatedly)
swml_not_allowedAn SWML action was requested but swaig_allow_swml is false
llm_errorThe model call returned an error. detail carries the provider’s error message
webhook_http_failureA tool’s webhook request failed. Carries the function name and the http_code returned
event_log_truncatedThe sidecar’s event log reached its size limit and older entries were dropped

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 following fields in the POST body:

function
string

The name of the function the model is calling.

argument
string | object

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.

global_data
object

The sidecar’s current global_data.

channel_data
object

Call identifiers: call_id, caller_id_name, caller_id_number, and destination_number.

project_id
string

Your SignalWire project ID.

space_id
string

Your SignalWire space ID.

Request (JSON)
1{
2 "function": "lookup_competitor",
3 "argument": "{\"competitor\":\"ACME\"}",
4 "global_data": { },
5 "channel_data": {
6 "call_id": "...",
7 "caller_id_name": "...",
8 "caller_id_number": "...",
9 "destination_number": "..."
10 },
11 "project_id": "...",
12 "space_id": "..."
13}

Response

Your webhook must return a JSON object with the result and any actions to take:

response
string

The result the model sees. It becomes the tool result the model reads on its next step.

action
object | object[]

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.

Response (JSON)
1{
2 "response": "ACME charges $99/seat. We're $79.",
3 "action": [
4 { "user_event": { "topic": "sidecar.alert", "level": "info" } },
5 { "set_global_data": { "last_lookup": "ACME" } }
6 ]
7}

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
object

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
object

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
string | object

The key to remove from global_data, or a new object to replace it.

action[].set_meta_data
object

A key-value object of session metadata.

action[].unset_meta_data
string | object

The metadata key to remove, or a new object to replace it.

action[].transfer
object

Transfers the call to a destination. Ends the sidecar and transcription.

transfer.dest
string

The destination — a phone number, SIP URI, or SWML URL.

transfer.summarize
booleanDefaults to false

Whether to include a conversation summary when transferring.

action[].hangup
boolean

When true, hangs up the call. Ends the sidecar and transcription.

action[].stop
boolean

When true, stops the sidecar only; transcription continues.

action[].say
string

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
object[]

Enables or disables functions mid-conversation. Each entry sets a function’s active state.

toggle_functions[].function
stringRequired

The name of the function to toggle.

toggle_functions[].active
booleanDefaults to true

Whether to enable or disable the function.

action[].settings
object

Changes the sidecar’s model settings, such as the model or temperature. Gated by swaig_allow_settings.

action[].SWML
object

A SWML document to run on the call. Gated by swaig_allow_swml. The transfer: true variant ends the sidecar.

action[].user_input
string

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
boolean | string

Controls whether the model may chain tool calls — true, or "forever" for unlimited chaining.

action[].extensive_data
boolean

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_sidecar and plain live_transcribe cannot 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, only remote-caller) is rejected.
  • Turn detection is tuned for Deepgram. 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.
  • The sidecar stops with the call. When the call hangs up or transcription stops, the sidecar’s final callback fires and it stops.
  • sidecar_skip is 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 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.

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.

1version: 1.0.0
2sections:
3 main:
4 - answer: {}
5 - ai_sidecar:
6 prompt: "You are a real-time sales copilot. After each customer turn, give the agent one concise piece of advice or call sidecar_skip if no advice is needed."
7 lang: "en-US"
8 url: "https://your-app.example.com/sidecar/events"
9 hints: ["ACME", "Globex", "FedRAMP", "SOC 2"]
10 SWAIG:
11 defaults:
12 web_hook_url: "https://your-app.example.com/sidecar/swaig"
13 functions:
14 - function: lookup_account
15 description: "Look up an account record."
16 parameters:
17 type: object
18 properties:
19 customer_id:
20 type: string
21 required:
22 - customer_id
23 - connect:
24 from: "+15555550100"
25 to: "+15555550199"
26 answer_on_bridge: true
27 - hangup: {}