Twilio Voice Handler
Inbound gateway for Twilio telephony events — voice calls, SMS inbound/status, recording completions, and transcriptions. Handles URL-encoded POST bodies (Twilio standard), verifies HMAC-SHA1 signatures with dual-host URL candidates, and writes to Supabase CCP + local JSONL call logs.
Endpoint
| Path | Provider | Status |
|---|---|---|
https://webhook.reri.co/sms | Twilio SMS prefix | ✅ approved |
https://webhook.reri.co/sms/webhook | Twilio SMS inbound | ✅ approved |
https://webhook.reri.co/sms/status | Twilio SMS status callback | ✅ approved |
https://webhook.reri.co/voice | Twilio voice prefix | ✅ approved |
https://webhook.reri.co/voice/webhook | Twilio voice inbound | ✅ approved |
https://webhook.reri.co/voice/status | Twilio voice status callback | ✅ approved |
https://webhook.reri.co/voice/recording-complete | Twilio recording done | ✅ approved |
https://webhook.reri.co/voice/transcription | Twilio transcription done | ✅ approved |
Port: 18797 (systemd unit: twilio-webhook.service)
Tunnel: Cloudflare Tunnel reri-api → webhook.reri.co (canonical); Tailscale Funnel as fallback (untested for path-strip; HMAC URL candidate must match)
Auth method
HMAC-SHA1 (Twilio uses SHA1, not SHA256). Twilio computes HMAC-SHA1(AUTH_TOKEN, full_url + sorted_post_params) and sends it in x-twilio-signature. The handler tries two URL candidates per request:
https://srv1347501.tailb025a7.ts.net+ path (Tailscale)https://webhook.reri.co+ path (Cloudflare canonical)
This dual-host approach handles rollback scenarios where Twilio is still configured against the Tailscale URL. Secret from TWILIO_AUTH_TOKEN env var.
Body format: application/x-www-form-urlencoded (Twilio standard) — handler uses express.urlencoded({ extended: false }).
Payload shape
| Field | Type | Description |
|---|---|---|
CallSid | string | Unique Twilio Call identifier |
SmsSid | string | Unique Twilio SMS identifier |
Body | string | Message content (SMS) or transcription text |
Direction | string | inbound or outbound-api |
From | string | Caller/sender E.164 phone number |
To | string | Recipient E.164 phone number |
CallStatus | string | ringing, in-progress, completed, etc. |
RecordingUrl | string | (recording events) URL of MP3 recording |
TranscriptionText | string | (transcription events) Full transcript text |
Downstream dispatch
twilio-voice-handler.js
├─ /voice/webhook (inbound call) → generate TwiML response
├─ /voice/recording-complete → write calls.jsonl + Supabase
├─ /voice/transcription → Supabase CCP (svueekfvfrvhylxygktb)
├─ /sms/webhook (inbound SMS) → Supabase message write
└─ /sms/status → Supabase delivery status update
Call log path: voice-calls/calls.jsonl (local append, NDJSON format)
Supabase project: svueekfvfrvhylxygktb (CCP production)
Dedup strategy
Uses checkDedupRaw() from scripts/lib/webhook-dedup.js — raw variant that works without Express middleware chain. Keyed on CallSid or SmsSid. Failed/unprocessable payloads written to data/dead-letters/.
Audit trail
webhook_audit_log— viaauditLogRaw()fromscripts/lib/webhook-audit.js. Raw variant used because handler parses URL-encoded bodies outside normal middleware.- Call logs:
voice-calls/calls.jsonl— local NDJSON for per-call audit trail. - Dead letter queue:
data/dead-letters/for unprocessable events. - Fatal error handler:
uncaughtException+unhandledRejectionlog structured JSON before process exit.
Related
- webhook-architecture — Cross-handler governance; note: Twilio uses HMAC-SHA1 (not SHA256), dual-host URL candidates required
- twilio — Twilio API reference, account SID, auth token rotation, TwiML spec, recording/transcription endpoints
- _summary — Aurora may consume transcription results dispatched from this handler
- cron-timer-registry —
twilio-webhook.servicesystemd unit, restart policy - backend-model-map — Gateway routing for voice event processing