Recording and Transcribing Calls in SWML
SignalWire offers two recording verbs and one transcription verb. Pick the right one based on whether you need to keep the call going while recording, and whether you need real-time transcripts.
| Verb | Mode | Use for |
|---|---|---|
| record_call | Background, non-blocking | Dashboards, analytics, AI-assisted calls. Runs alongside ai. |
| record | Foreground, blocking | Voicemail — call pauses until recording ends. |
| live_transcribe | Background | Real-time transcript streaming to your webhook. |
| tap | Background | Fork audio to a third-party endpoint (monitoring, fraud detection). |
record_call — background dual-channel recording
version: 1.0.0
sections:
main:
- answer: {}
- record_call:
format: mp3 # "wav" | "mp3" | "mp4"
stereo: true # required for speaker diarization
direction: both # "speak" | "listen" | "both"
beep: false
input_sensitivity: 44.0
initial_timeout: 0
end_silence_timeout: 0
max_length: 3600
status_url: "https://your.api/webhooks/recording"
- ai:
prompt: { text: "You are a sales analyst..." }
record_call is non-blocking — it runs in the background while subsequent verbs (ai, connect, etc.) execute on top of it. Always set stereo: true if you need to separate speakers later. Without stereo both voices mix into one channel and per-speaker sentiment analysis becomes impossible.
Variables set
| Variable | Description |
|---|---|
| ${record_call_url} | URL to download the recording |
| ${record_call_result} | success or failed |
| ${record_control_id} | Control ID for stop_record_call |
Stopping it mid-call
- stop_record_call: {}
record — foreground voicemail-style recording
record blocks the call until the recording ends. Use for voicemail.
- record:
stereo: true
format: wav
direction: speak # default — caller's voice only
beep: true
end_silence_timeout: 5.0
terminators: "#"
max_length: 120
status_url: "https://your.api/webhooks/recording"
Variables set
| Variable | Description |
|---|---|
| ${record_url} | URL of the foreground recording |
| ${record_result} | success or failed |
Recording status webhook payload
status_url receives this JSON when recording state changes:
{
"event_type": "calling.call.record",
"timestamp": 1640000000.123,
"project_id": "PROJECT-UUID",
"params": {
"state": "finished",
"control_id": "CONTROL-UUID",
"call_id": "CALL-UUID",
"url": "https://your-space.signalwire.com/api/v1/recordings/rec-uuid/download",
"duration": 125.4,
"size": 2048000,
"recording_id": "REC-UUID",
"start_time": 1640000000.0,
"end_time": 1640000125.4,
"record": {
"audio": { "format": "mp3", "direction": "both", "stereo": true }
}
}
}
params.state values: recording, paused, finished, no_input, error.
live_transcribe — real-time transcripts
Streams partial and final ASR results to your webhook as the conversation happens. Backed by Deepgram (default) or Google.
version: 1.0.0
sections:
main:
- answer: {}
- live_transcribe:
action:
start:
webhook: "https://your.api/webhooks/transcription"
lang: en # required ISO code
live_events: true # partial results in real time
ai_summary: true # AI summary at session end
ai_summary_prompt: "Summarize key points, intent, and action items."
direction:
- remote-caller # the inbound caller
- local-caller # your AI or agent
speech_engine: deepgram # "deepgram" (default) | "google"
speech_timeout: 60000 # ms, min 1500
vad_silence_ms: 300 # ms silence to end utterance (300 deepgram, 500 google)
vad_thresh: 400 # VAD sensitivity 0-1800
Stop, summarize, or restart mid-call
- live_transcribe: { action: { stop: {} } }
- live_transcribe: { action: { summarize: {} } }
Direction values
remote-caller— the inbound caller's audio.local-caller— your local party (AI or human agent).
Include both to transcribe the full conversation.
tap — fork audio to a third party
Sends a copy of the audio stream to another endpoint for live monitoring, fraud detection, or compliance.
- tap:
uri: "rtmps://stream.example.com/live/key"
direction: both
codec: PCMU
Tap can target RTMP, WebSocket, or another SIP endpoint. The original call continues unaffected.
Concurrent verbs — recording + AI + live transcript
All three are non-blocking. Stack them.
version: 1.0.0
sections:
main:
- answer: {}
- record_call:
format: mp3
stereo: true
status_url: "https://your.api/webhooks/recording"
- live_transcribe:
action:
start:
webhook: "https://your.api/webhooks/transcript"
lang: en
live_events: true
direction: [remote-caller, local-caller]
- ai:
prompt: { text: "You are an HVAC dispatcher." }
post_prompt:
text: "Return JSON with intent, address, scheduled."
post_prompt_url: "https://your.api/webhooks/post-prompt"
This is the canonical Call Intelligence pipeline shape.
Anti-patterns
- Using
recordwhen you also needai—recordblocks. Userecord_callinstead. stereo: falsefor sentiment dashboards — kills speaker diarization.- Missing
status_url— you get a recording but no callback that tells you it's ready. - Setting
live_transcribedirectionto onlyremote-callerwhen you want both sides — you'll only see what the caller said, not what the AI replied. - Setting
vad_silence_msbelow 250ms with Deepgram — splits utterances mid-sentence. - Setting
max_lengthtoo high (3600+) — you'll have multi-GB recordings to ferry.