Maintenance
Scheduled jobs
| Cron | Model / Code | Interval | Purpose |
|---|---|---|---|
| Fetch Call Prices from Twilio | connect.call → fetch_call_prices_batch() |
every 5 minutes | Fetches per-call cost from the Twilio REST API for completed calls that have not been priced yet. |
The job is always scheduled, but it only does work when Fetch Call Prices is
enabled in settings. Twilio populates call prices
asynchronously after a call ends, which is why this runs as a deferred batch
rather than at call-completion time. Priced calls carry price, price_unit
(currency code) and price_currency (symbol) on connect.call.
To change the cadence, edit the cron under Settings ▸ Technical ▸ Scheduled Actions (developer mode).
Licensing
Twilio is a licensed Oduist module. Outbound origination calls
oduist.license.check_license("connect") before placing a call, so an invalid or
expired license blocks click-to-call. The post_init_hook refreshes the license
status on install. If click-to-call fails with a license error, verify the
connect license in the core settings.
Recordings & transcription
- Twilio recording webhooks (
/twilio/webhook/recordingstatus) createconnect.recordingentries; runtime start/stop from the web phone uses the shared softphone recording RPCs and Twilio's Recording API. - Transcription and summarization are not in this module. OpenAI
transcription (Whisper + GPT summary) lives in core
connectbecause it is provider-agnostic. Configure it in the core settings, not here.
A recording plays as 0 seconds
The metadata (duration, price, caller) comes from the Twilio API and is correct; the audio itself never arrived. The Odoo log names the URL and the status the store answered.
Playback goes through the media proxy: the player asks Odoo for the file and
Odoo fetches it, using the account credentials when Twilio requires them
(connect.settings.get_media_auth(), ADR-060). Nothing extra is needed for a
standard account.
The usual cause is External Storage on the Twilio account (Voice >
Settings > Recording storage): Twilio writes the audio to your own S3 bucket
and keeps only the metadata, so the Twilio API no longer serves the file and
the bucket rejects an unauthenticated request. Either turn External Storage
off so Twilio stores the media, or install connect_s3, which owns that
setup and reads the audio back from the bucket.
Troubleshooting
| Symptom | Check |
|---|---|
| Sync fails with "Error authenticating requests to the Twilio API" | Wrong Account SID / Auth Token (Twilio error 20003). Re-enter credentials. |
| Sync refuses to run | Account SID / Auth Token empty, or the core API URL is invalid. |
| Webhooks do nothing / log "Twilio request is not valid!" | Signature mismatch — confirm the Auth Token matches the account and that Odoo is reachable over the exact public HTTPS URL configured as API URL. |
| Webhooks log "Twilio requires HTTPS to be setup!" | Odoo is being reached over plain HTTP. Terminate TLS in front of Odoo and use an https:// API URL. |
| Web phone will not register | Verify API Key SID / Secret in settings and that Web Phone Enabled is on for the user. |
| Outbound call raises "User does not have a SIP username defined!" | The user has no PBX (connect_user) profile / username. Fill in the Twilio user fields. |
| Call prices never appear | Enable Fetch Call Prices; prices arrive on the next 5-minute cron run after Twilio finalizes them. |
| Balance shows "Balance API not available" | The Twilio account type/region does not expose the balance endpoint; this is informational, not an error. |
Where to look
- Odoo server log — webhook signature errors and Twilio API exceptions are
logged by the
connect_twilio.controllers.twilio_webhooksand model loggers. connect.debug— the core debug model records origination TwiML and other diagnostic traces (daily cron cleanup).- Twilio Console ▸ Monitor ▸ Logs — Twilio's own call/message/error logs are the authoritative source for what Twilio sent and received.