T
Telephony SOPKnowledge Base
Search
← All topics

SWML Record and Transcribe — Background Recording, Live Transcription, Tap

runnable

Record calls with `record_call` (background, stereo) and `record` (foreground, voicemail). Stream real-time transcripts via `live_transcribe`. Fork audio to a third party with `tap`. Includes status webhook contract and Deepgram/Google speech-engine config.

signalwireswmlrecordingtranscriptionlive-transcribetap
Agent trigger phrases: record call SWML · record_call vs record · live_transcribe webhook · Deepgram in SignalWire · stereo recording stereo true · stop_record_call · tap SWML · speaker diarization SignalWire

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 record when you also need airecord blocks. Use record_call instead.
  • stereo: false for sentiment dashboards — kills speaker diarization.
  • Missing status_url — you get a recording but no callback that tells you it's ready.
  • Setting live_transcribe direction to only remote-caller when you want both sides — you'll only see what the caller said, not what the AI replied.
  • Setting vad_silence_ms below 250ms with Deepgram — splits utterances mid-sentence.
  • Setting max_length too high (3600+) — you'll have multi-GB recordings to ferry.

See also