openclaw - 💡(How to fix) Fix voice-call: support Twilio AMD on outbound + dynamic mode switch when machine detected

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Outbound conversation mode works fine when a human picks up, but degrades badly when the carrier rolls the unanswered call to voicemail. The plugin currently has no awareness of human vs machine answer — Twilio's Answering Machine Detection (AMD) isn't requested on outbound init, and there's no path to transition a live call's mode after the answer event fires.

Use case: notification bot that should behave conversationally on human pickup but cleanly leave a VM otherwise.

Root Cause

Root cause (paths from ~/.openclaw/npm/node_modules/@openclaw/voice-call/dist/)

Fix Action

Fix / Workaround

Workaround possible inside our control surface?

Code Example

"outbound": {
  "defaultMode": "conversation",
  "notifyHangupDelaySec": 2,
  "machineDetection": "Enable",          // "off" | "Enable" | "DetectMessageEnd"
  "amdTimeoutSeconds": 30,
  "amdSpeechThresholdMs": 2400,
  "amdSpeechEndThresholdMs": 1200,
  "amdSilenceTimeoutMs": 5000
}
RAW_BUFFERClick to expand / collapse

Summary

Outbound conversation mode works fine when a human picks up, but degrades badly when the carrier rolls the unanswered call to voicemail. The plugin currently has no awareness of human vs machine answer — Twilio's Answering Machine Detection (AMD) isn't requested on outbound init, and there's no path to transition a live call's mode after the answer event fires.

Use case: notification bot that should behave conversationally on human pickup but cleanly leave a VM otherwise.

Root cause (paths from ~/.openclaw/npm/node_modules/@openclaw/voice-call/dist/)

  1. No AMD request on outbound init: twilio-D-QbSGSk.js:483-498 passes only To, From, StatusCallback, StatusCallbackEvent=[initiated,ringing,answered,completed], Timeout=30, Twiml|Url to /Calls.json. Confirmed by grep of entire dist: zero MachineDetection / AsyncAmd / AmdStatusCallback / MachineDetectionTimeout / MachineDetectionSpeechThreshold references.

  2. AnsweredBy never parsed: twilio-D-QbSGSk.js:317-369 (normalizeEvent) reads CallStatus, SpeechResult, Digits — never AnsweredBy. Even if AMD were enabled upstream, the result would be discarded.

  3. Strict schema blocks config-only fix: config-Beumk4ED.js:171-179OutboundConfigSchema is .strict() with only defaultMode and notifyHangupDelaySec. Adding machineDetection: ... to openclaw.json is rejected at config-load.

  4. No mode-transition surface: runtime-entry-CQfEI6TJ.js:1128-1139 (maybeSpeakInitialMessageOnAnswered) reads mode once at call.answered and never re-evaluates. No event type for "answered by machine." notifyHangupDelaySec auto-hangup at line 613-622 is a fire-once timer keyed to mode === "notify" at speak-time.

Why this breaks the ring-then-VM path

In conversation + realtime.enabled mode, the outbound webhook returns <Connect><Stream> TwiML on the first webhook hit. Twilio establishes the WSS upgrade only AFTER the call is answered. During ringing, no media stream exists — expected. When VM picks up: Twilio answers, opens WSS to the realtime handler, but the realtime model has no situational info that this is a machine. Instructions tell it to wait for a beep before delivering; in practice it sits silent waiting for human audio that never reaches VAD threshold (VM is playing its outgoing message at carrier-level, not on the bridged media).

With AMD enabled + parsed, the bridge could know "machine answered" and switch to a VM-delivery flow immediately.

Requested behavior

Add outbound.machineDetection (and companion knobs) to the config schema, pass them to Twilio on outbound init, parse AnsweredBy from the AMD callback, emit a new event for machine-answered, and provide a path to transition a live conversation-mode call to "notify-after-answer" sub-state on that event.

Suggested API shape

"outbound": {
  "defaultMode": "conversation",
  "notifyHangupDelaySec": 2,
  "machineDetection": "Enable",          // "off" | "Enable" | "DetectMessageEnd"
  "amdTimeoutSeconds": 30,
  "amdSpeechThresholdMs": 2400,
  "amdSpeechEndThresholdMs": 1200,
  "amdSilenceTimeoutMs": 5000
}

Suggested lifecycle

  1. When set, pass MachineDetection + AMD params to Twilio on initiateCall and register AmdStatusCallback pointing back to the webhook URL.
  2. Parse AnsweredBy from the AMD callback. Emit new event call.answered_by_machine carrying the AMD result string.
  3. When a conversation-mode call receives call.answered_by_machine, transition to a "notify-after-answer" sub-state: speak stored initialMessage via existing speak() path then auto-hangup after outbound.notifyHangupDelaySec. If call.answered (human) fires first, stay in conversation mode.

Workaround possible inside our control surface?

No. Schema is strict. Lifecycle hooks are private. This needs to land upstream. A clawd-side Twilio webhook proxy could inject AMD params + intercept the callback (~150 LOC HTTP plumbing) but creates a maintenance liability — not pursuing without further direction.

Severity

High. Core feature gap blocking notification bots that need to gracefully handle "human or VM" outbound calls. Adjacent to companion issue on end_call realtime-tool handler binding (filed simultaneously) — even with this issue fixed, clean VM-post-delivery termination needs that one too.

Environment

  • OpenClaw 2026.5.10-beta.5
  • macOS 14+
  • Outbound provider: Twilio Programmable Voice + Media Streams + OpenAI Realtime

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix voice-call: support Twilio AMD on outbound + dynamic mode switch when machine detected