Webhooks & Security
Public URL requirement
Twilio delivers every voice/message/recording event to Odoo over HTTP webhooks. Your Odoo instance must be reachable from the internet over HTTPS. Set the core API URL (Connect ▸ Configuration ▸ Settings) to the public URL — Connect builds every webhook URL it pushes to Twilio from this value.
Twilio requires HTTPS for signature validation; if Odoo is only reachable over plain HTTP, signature checks fail and the log shows "Twilio requires HTTPS to be setup!".
Webhook routes
All routes are under /twilio/webhook/, declared type='http', auth='public',
csrf=False, and run as the dedicated webhook user
(connect.user_connect_webhook).
| Route | Purpose |
|---|---|
POST /twilio/webhook/callstatus |
Call status callbacks (initiated / answered / completed). |
POST /twilio/webhook/callaction |
Call action handling. |
POST /twilio/webhook/<model_name>/call_action/<record_id> |
Model-specific call action (legacy connect.callflow names are remapped to connect.twilio.callflow). |
POST /twilio/webhook/number |
Inbound call to a Twilio number → routes to destination. |
POST /twilio/webhook/twiml/<twiml_id> |
Renders a TwiML application. |
POST /twilio/webhook/callflow/<flow_id>/gather |
Call-flow <Gather> result (DTMF/speech). |
POST /twilio/webhook/domain |
Inbound SIP-domain call routing. |
POST /twilio/webhook/recordingstatus |
Recording status callback. |
POST /twilio/webhook/vm_recordingstatus |
Voicemail recording callback. |
POST /twilio/webhook/message |
Incoming SMS / WhatsApp. |
POST /twilio/webhook/message_status |
Message delivery status. |
POST /twilio/webhook/outgoing_callerid |
Caller-ID validation status. |
Signature validation
Every webhook first runs check_signature():
- When Verify Twilio Requests (
twilio_verify_requests) is on (the default), the controller validates theX-Twilio-Signatureheader withtwilio.request_validator.RequestValidator, using the Auth Token read viasudo(). The request URL is forced tohttps:for the signature computation. check_signature()validates against the POST form body only (request.httprequest.form), never the merged route kwargs: Twilio signs the full URL (query string included) plus the POST parameters, and Odoo folds the query string into the kwargs, so validating with those would count every query parameter twice and reject any webhook URL that carries one.- When validation fails, voice endpoints return
<Response><Say>Invalid Twilio request!</Say></Response>and status endpoints returnFalse; nothing is processed. - When the option is off, validation is skipped (development only — keep it on in production).
Do not disable verification in production
With verification off, anyone who can reach the public webhook URLs can inject call/message/recording events. Only turn it off temporarily for local debugging.
Security groups & access
The module reuses the core Connect groups:
- Connect User (
connect.group_user) — read access to Twilio config models. - Connect Administrator (
connect.group_admin) — full CRUD; required for the Configuration menu, WhatsApp senders/templates, and message configuration. - Connect Webhook (
connect.group_webhook) — the identity of the public webhook controllers.
Model access (security/access_rules.xml):
| Model | User | Admin | Webhook |
|---|---|---|---|
connect.twilio.exten |
Read | Full | Read |
connect.twilio.callflow |
Read | Full | Read |
connect.twilio.callflow_choice |
Read | Full | Read |
connect.twilio.number |
Read | Full | Read |
connect.twilio.outgoing_callerid |
Read | Full | Read + Write |
connect.twilio.user_callflow |
Read | Full | — |
connect.twilio.user_callflow_call |
Read | Full | — |
connect.twilio.message_configuration |
— | Full | — |
connect.twilio.twiml |
Read | Full | Read |
connect.twilio.domain |
Read | Full | Read |
connect.whatsapp_sender |
Read | Full | Read + Write + Create |
connect.message_content_template |
Read | Full | — |
The Auth Token and API Secret are additionally protected at the field
level (base.group_erp_manager only) and are never returned to the webhook
identity; they are masked (****) for non-managers.