Channels
Voice
Twilio Voice form webhook signed with X-Twilio-Signature. Post and Reply plan as TwiML <Say>. The HTTP layer writes that markup back as text/xml; it does not dispatch an outbound API call.
Point Twilio’s Voice webhook at a public HTTPS URL. Enable speech recognition so inbound includes SpeechResult. Use overlap: "drop" so a barge-in does not queue behind the current turn — see Overlap.
export TWILIO_ACCOUNT_SID='…'
export TWILIO_AUTH_TOKEN='…'
export VOICE_WEBHOOK_URL='https://…/voice'
export PORT=8080cx.channels.add(
"voice",
via="self-host",
account_sid=account_sid,
auth_token=auth_token,
webhook_url=webhook_url,
bot_token="local",
)
@cx.on_message({"channel": "voice", "overlap": "drop"})
def on_speech(thread, msg, ctx):
thread.post(msg.text)
# POST form -> cx.handle("voice", body, headers)
# respond with TwiML text/xml on that HTTP frontawait cx.channels.add("voice", {
via: "self-host",
account_sid,
auth_token,
webhook_url,
bot_token: "local",
})
cx.onMessage({ channel: "voice", overlap: "drop" }, async (thread, msg) => {
await thread.post(msg.text)
})
// POST form → await cx.handle("voice", body, headers)
// respond with TwiML text/xml on that HTTP frontThe URL in add() must be the public URL Twilio signed.