Implementing Twilio-Based SMS Relay for Multi-Carrier Call Forwarding

What Was Done

We integrated Twilio as an SMS relay layer to solve a carrier-level limitation that prevented cascading call forwarding across multiple phone numbers. Previously, the Queen of San Diego infrastructure relied on native carrier call forwarding (QDN line → Sergio main → backup 858-335-4807), but the carrier explicitly blocked this chaining at the protocol level. By introducing Twilio, we now have a programmable intermediary that can receive inbound messages and intelligently route them to multiple endpoints without carrier restrictions.

The Twilio credentials were securely stored in the shared secrets repository and documented in a machine-readable reference file for future development sessions.

Technical Details

Credential Management & Storage

Rather than embedding credentials in environment variables or configuration files scattered across multiple machines, we adopted a centralized secret management approach:

  • Primary storage: /Users/cb/Documents/repos/.secrets/repos.env (file mode 600, read-write for owner only)
  • Variables appended:
    • TWILIO_ACCOUNT_SID
    • TWILIO_AUTH_TOKEN
  • Reference memory: /Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/reference_twilio_credentials.md
  • Session memory: Updated /Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/MEMORY.md

The reference file is intentionally separate from the actual credentials file. It contains only the structure and purpose (where to find credentials, which variables to use, how to authenticate), allowing future development sessions to locate and use the credentials without redundantly copying sensitive data into session logs or chat histories.

Why Twilio Over Native Call Forwarding

Carrier-level call forwarding is stateless and limited. AT&T, Verizon, and other carriers implement call forwarding as a simple PSTN routing rule: when call arrives on X, ring Y. However, these rules are typically sequential and cannot handle the "if Y doesn't answer in 10 seconds, try Z" logic we needed. Additionally, carrier documentation explicitly forbids chaining forwarding rules (A → B → C) to prevent routing loops and fraud.

Twilio provides:

  • Programmable logic: IVR, conditional routing, and queuing via TwiML (Twilio Markup Language)
  • Failover capability: Ring multiple numbers in parallel or sequence with custom timeout logic
  • Audit trails: Full call/SMS logs via REST API
  • SMS-specific features: Message routing, forwarding, and reply handling without PSTN constraints

Infrastructure Architecture

Call/SMS Inbound Routing Flow


┌─────────────────────────┐
│ Inbound Call/SMS to     │
│ QDN Twilio Number       │
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────────────────────┐
│ Twilio Webhook Receiver                 │
│ (Awaiting TwiML handler implementation)  │
└────────────┬────────────────────────────┘
             │
             ▼
┌──────────────────────────────┐
│ Conditional Routing Logic:   │
│ 1. Try Sergio main number    │
│    (timeout: 10s)            │
│ 2. Fallback: 858-335-4807    │
│    (backup number)           │
│ 3. If both fail: Voicemail   │
└─────────────────────────────┘

Credentials Retrieval at Runtime

Any service needing to call Twilio REST API should load credentials from the environment:


# Example: Python service initialization
import os
from twilio.rest import Client

account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')

client = Client(account_sid, auth_token)

On deployment to production servers (e.g., jada-agent daemon running on Lightsail), the .secrets/repos.env file must be sourced before launching application services:


source /path/to/.secrets/repos.env
python3 /opt/jada-agent/main.py

Key Decisions & Rationale

Why Store Secrets Separately from Code

Keeping TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in a dedicated .secrets/ directory prevents accidental commits to version control. We enforced file permissions (mode 600) so only the owner can read/write, reducing the surface area if a machine is compromised. The reference file in memory allows the next session to know where to find secrets without storing them in the memory itself.

Reference Memory as Session Bridge

Development sessions are ephemeral. By writing reference_twilio_credentials.md, we created a machine-readable artifact that persists across sessions and documents the credential structure without exposing sensitive values. Future developers (or agents) can read this file to understand the shape of the integration without needing to re-explain where credentials live.

SMS Relay Over SIP Trunking

For SMS specifically, Twilio is superior to traditional SIP trunking because:

  • SMS is inherently asynchronous; Twilio's queue-and-forward model handles out-of-order delivery gracefully
  • PSTN carriers treat SMS separately from voice; routing voice through SIP doesn't automatically extend to SMS
  • Twilio's REST API simplifies sending SMS from web applications (e.g., sending alerts from maintenance hub, event notifications)

What's Next

Immediate Action Items

  • Implement TwiML handler: Build the webhook endpoint that receives Twilio inbound events and returns TwiML instructions (parallel ring logic for voice, conditional routing for SMS)
  • Test failover scenarios: Verify that if Sergio's main number doesn't answer, the call/SMS reaches the backup (858-335-4807) within expected timeouts
  • Deploy to production: Once tested in staging, add Twilio credentials to production Lightsail instance and update service startup scripts
  • Audit logging: Wire Twilio call/SMS logs into the existing logging infrastructure so on-call engineers can troubleshoot routing failures

Future Enhancements

  • IVR menu: Route different call types (event inquiries, maintenance alerts, booking questions) to different destinations
  • SMS auto-reply: Acknowledge inbound SMS and provide status (e.g., "Your maintenance request was received and assigned to [team]")
  • Multi-region failover: Expand backup routing beyond a single number to include regional representatives

Related In-Progress Work

This Twilio integration unblocks the