```html

Integrating Twilio for Multi-Carrier SMS Relay: Architecture & Implementation

What Was Done

We integrated Twilio into the Queen of San Diego infrastructure to enable carrier-level SMS relay functionality. Specifically, this solves a constraint where direct carrier forwarding wasn't possible at the telecom level — we needed an intermediary that could accept inbound SMS to a Twilio-provisioned number and intelligently route it downstream to our backup contact chain.

The immediate use case: when the QDN line receives an incoming message, it now routes through Twilio's webhook infrastructure to our backend, which then cascades the message to Sergio (primary) and his backup contact at 858-335-4807 (secondary).

Technical Details: Credential Management & Environment Setup

Twilio credentials were added to our centralized secrets management at /Users/cb/Documents/repos/.secrets/repos.env with restrictive file permissions (mode 600). This follows the principle of least privilege and keeps credentials out of version control.

Three Twilio resources were provisioned:

  • Account SID: The primary identifier for the Twilio account (organization-level resource)
  • Auth Token: The master authentication credential for API calls
  • API Key (type: Main): A scoped credential for programmatic access, allowing fine-grained permission delegation if needed

All credentials are loaded at runtime via environment variable injection, not hardcoded. In development, we use a local .env file; in production, these are injected via container secrets or Lambda environment variables depending on the compute layer.

Reference documentation was created at /Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/reference_twilio_credentials.md to ensure future engineering sessions know exactly where these credentials live and how to rotate them.

Infrastructure Architecture

The Twilio integration sits at the ingress layer of our SMS pipeline:


Inbound SMS (to Twilio number)
    ↓
Twilio Inbound Webhook Handler
    ↓
Backend API (processes TwiML response)
    ↓
Routing Logic (QDN line → Sergio → Backup contact)
    ↓
Twilio Outbound API (sends cascaded SMS)

The webhook endpoint on our backend will accept POST requests from Twilio with the following parameters: From (sender), To (our Twilio number), Body (message content), and MessageSid (idempotency key). Our handler immediately returns a TwiML response (either acknowledgment or error state) to terminate Twilio's retry logic.

Asynchronously, we then invoke Twilio's Outbound Messaging API to send the cascaded SMS. This decouples the webhook response from the relay logic, reducing latency and allowing retry logic to live in our application layer rather than Twilio's.

Why This Approach

Carrier-level limitations: Most telecom carriers don't expose APIs to cascade calls or SMS programmatically. Twilio abstracts this — their network agreements with carriers allow us to send/receive SMS at scale without maintaining direct relationships with Verizon, AT&T, etc.

Webhook-first design: Rather than polling or maintaining a persistent connection, Twilio pushes inbound messages to our backend via HTTPS webhook. This is event-driven, scales naturally, and avoids the complexity of managing connection state.

Centralized secrets management: By storing all three credential types in repos.env with mode 600, we ensure:

  • Only the owning user can read the file (no accidental exposure via git diffs or log files)
  • Credentials are rotated in one place; all services consume from the same source
  • Future team members have a documented location to check before hardcoding

API Key vs. Auth Token: The Auth Token is a master credential — it grants full account access. The API Key (type: Main) is a secondary credential that can be scoped to specific products (SMS, Voice, etc.) in Twilio's console. We document both so that future integrations (e.g., Twilio Voice) can use appropriately scoped keys.

Workflow: Building the Twilio Relay

With credentials now in place, the next steps are:

  • Backend webhook handler: Create an endpoint (e.g., POST /api/sms/inbound) that validates Twilio's request signature (using the Auth Token and request body), parses the inbound SMS, and logs it to a queue or database.
  • Cascade logic: Implement a function that takes the parsed SMS and invokes Twilio's Outbound Messaging API to send to Sergio, then (on his non-response within N seconds) sends to the backup contact. This can live in a background job or a step function.
  • Idempotency: Use Twilio's MessageSid as a deduplication key. If we receive the same webhook twice (Twilio retrying due to timeout), we should not send duplicate cascades.
  • Monitoring: Log all inbound/outbound SMS to CloudWatch (or equivalent) with the MessageSid, timestamps, and delivery status. Set alarms for webhook failures or unusually high cascading rates.

Key Decisions & Trade-offs

Webhook vs. Pull: We chose webhooks over polling because Twilio guarantees delivery within seconds, whereas polling introduces latency and unnecessary API calls. Twilio will retry failed webhooks with exponential backoff for 24 hours.

Asynchronous relay: Returning the TwiML response immediately (before cascading to Sergio) ensures Twilio doesn't retry the webhook while we're still processing. The relay happens in a separate background task, decoupling the two systems.

Credential segregation: We store creds in .secrets/repos.env rather than a dedicated Twilio config file because this environment already powers other integrations (Stripe, Google APIs, etc.). Centralization reduces the attack surface.

What's Next

With the Twilio infrastructure in place, the next phase is:

  • Build the webhook handler in the main backend codebase, likely in handlers/sms_inbound.py or equivalent.
  • Implement request signature validation per Twilio's security docs.
  • Test the end-to-end flow: send an SMS to the Twilio number, verify it arrives at Sergio, and confirm cascading to the backup contact works.
  • Wire in monitoring and alerting for webhook failures, API rate limits, or message delivery failures.
  • Document the cascade timeout logic (how long we wait for Sergio's response before escalating to the backup contact).

This integration unblocks the QDN line's event notification pipeline and sets the foundation for other SMS-based features (password reset codes, appointment reminders, etc.) in future phases.

```