Skip to content

Twilio status callbacks

Twilio fires status callbacks for every SMS as it moves through their system. We verify, dedup, store, and use these for delivery confirmation + failure alerts.

Statuses received

  • queuedsendingsent (Twilio side)
  • delivered (carrier accepted)
  • failed / undelivered (carrier rejected)

Webhook handler

/webhook/twilio/status (src/routes/webhook-twilio-status.ts):

  1. Parse form-urlencoded body (Twilio uses form encoding, not JSON)
  2. Verify signature: HMAC-SHA1 of URL + sorted form params keyed by TWILIO_AUTH_TOKEN. X-Twilio-Signature header.
  3. Insert into twilio_events (dedup on (MessageSid, MessageStatus))
  4. Sync terminal-state to outbound_log:
    • deliveredmarkOutboundDelivered
    • failed / undeliveredmarkOutboundFailed with reason "failed (Twilio {ErrorCode}: {ErrorMessage})"
  5. Alert on terminal failure: SMS Bill with recipient + error

Outbound integration

sendSMS in src/twilio/client.ts automatically attaches StatusCallback={env.TWILIO_STATUS_CALLBACK_URL} if the env var is set. Currently:

TWILIO_STATUS_CALLBACK_URL=https://stayonthesnow-pms.minnetonka.workers.dev/webhook/twilio/status

(set via wrangler secret put).

Also records to outbound_log on send with channel='sms', message_id=result.sid, booking_id + purpose from the call’s params.

Schema (twilio_events)

event_sid, message_sid, status, recipient, error_code, error_message,
raw, received_at

event_sid = {MessageSid}:{MessageStatus} for dedup.

Inbound SMS

Separate endpoint: /webhook/sms/inbound (src/routes/webhook-sms.ts) handles incoming SMS to Bill’s Twilio number. Verifies signature, logs to migration_log, forwards via email to Bill.

Source

  • src/routes/webhook-twilio-status.ts — receiver
  • src/lib/twilio-signature.ts — HMAC-SHA1 helper
  • src/twilio/client.tssendSMS with auto-attach
  • migrations/0024_delivery_observability.sql (includes twilio_events)
  • Tests: test/routes/webhook-twilio-status.test.ts (13 cases)