```html

Building a Multi-Carrier SMS Relay for QuickDumpNow: Twilio Integration & CloudFront Tracking

What Was Done

This session completed infrastructure groundwork for QuickDumpNow's customer notification pipeline, specifically addressing a carrier-level constraint that prevented direct SMS cascading. The work involved:

  • Provisioning Twilio credentials and integrating account/API-key pairs into the deployment pipeline
  • Building a CloudFront-fronted tracking page (/track) to collect GPS telemetry and job status for four-trailer fleets
  • Extending the API Gateway surface with four new endpoints to support customer message delivery and trailer state persistence
  • Creating a CloudFront Function to rewrite tracking requests and manage CORS for the customer-facing tracking widget
  • Establishing reference memory for future sessions so Twilio credential rotation and SMS relay logic can be built without credential re-entry

Technical Details: Carrier Constraint & Twilio Solution

The original QDN handoff required a cascading SMS forward: customer receives a notification from one carrier line, then that message is forwarded to a backup contact (Sergio's team) via another carrier or internal routing. Testing revealed that this carrier-level forwarding was not supported by the upstream provider.

Why Twilio? Twilio's Programmable SMS API allows us to:

  • Receive inbound SMS from the primary carrier
  • Parse job ID and context from the message body
  • Query job state (status, location, ETA) from the QDN data layer
  • Re-send formatted, context-rich messages to backup contacts without carrier intermediation
  • Log all message interactions to S3 for compliance and debugging

This decouples the notification reliability from a single carrier's infrastructure and gives us programmatic control over retry logic and message enrichment.

Infrastructure: Credential Management & Secrets Rotation

Twilio provides two credential types for different purposes:

  • Account SID + Auth Token: For admin operations (phone number provisioning, webhook configuration, account introspection). These are stored in /Users/cb/Documents/repos/.secrets/repos.env with mode 600 (read-only to owner).
  • API Key + Secret: For SDK runtime operations in Lambda functions. Also stored in repos.env but marked for use in deployed function environment variables.

Why two pairs? API keys have finer-grained scope (e.g., "SMS sending only") and can be rotated without invalidating the master account token. If a Lambda is compromised, we can revoke the API key without taking down Twilio admin access.

Reference memory was written to /Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/reference_twilio_credentials.md to ensure future sessions don't re-enter credentials. The memory file maps each key type to its use case and points to the source of truth (repos.env, encrypted at rest in AWS Secrets Manager when deployed).

CloudFront Tracking Page & GPS Telemetry

A new tracking page was deployed to quickdumpnow.com/track to serve the customer-facing widget. This page:

  • Lives in the S3 bucket backing the quickdumpnow.com CloudFront distribution
  • Written to /Users/cb/Documents/repos/sites/quickdumpnow.com/track/index.html
  • Accepts ?job_id=<token> in the query string to look up a specific job
  • Polls the new /api/jobs/:job_id/location endpoint every 10 seconds for fresh GPS data
  • Displays ETA, current status (in transit, at site, en route to next), and trailer count

The page was deployed and CloudFront cache invalidated via:

aws s3 cp track/index.html s3://quickdumpnow.com/track/index.html
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/track/*"

API Gateway Extensions & Job State Persistence

Four new endpoints were added to the QDN API Gateway to support customer messaging and telemetry:

  • POST /api/jobs/:job_id/messages — Accept inbound SMS from Twilio webhook, parse job context, log to DynamoDB
  • GET /api/jobs/:job_id/location — Return current GPS coordinates, ETA, and status for the tracking page
  • POST /api/jobs/:job_id/location — Accept GPS telemetry from mobile units (trailer fleet), persist to DynamoDB with timestamp
  • GET /api/jobs/:job_id — Return full job state (customer name, pickup/dropoff address, assigned trailer, status, current location, messages)

Each endpoint includes an OPTIONS method to support CORS preflight requests from the tracking widget (which runs on customer domains, not quickdumpnow.com).

The Lambda function handling these routes lives at /Users/cb/Documents/repos/sites/dashboard.quickdumpnow.com/lambda/lambda_function.py. The function:

  • Validates JWT tokens embedded in the job_id query param (maps to a specific customer account)
  • Queries DynamoDB table qdn-jobs for job state
  • For location updates, writes to a separate qdn-job-locations table with a TTL of 7 days (old telemetry auto-expires)
  • Returns JSON responses with 5-minute CloudFront cache headers (tracks don't need sub-second freshness)

CloudFront Function for Request Rewriting

A CloudFront Function was created to handle rewrites for the /track path. This function:

  • Intercepts requests to quickdumpnow.com/track?job_id=...
  • Rewrites to the versioned S3 object key (e.g., track/index-v2.html) to support staged rollouts
  • Injects CORS headers so the page can fetch from the API Gateway (cross-origin requests)
  • Logs all tracking page views to CloudWatch for debugging and analytics

The function was published to the default behavior of the quickdumpnow.com distribution, ensuring all traffic to /track gets rewritten before hitting the origin.

Key Decisions

Why not SMS webhooks directly to API Gateway? Twilio's webhook payload includes metadata (message timestamps, carrier info, inbound phone number) that's valuable for debugging cascading failures. We buffer this in a Lambda-backed SQS queue before writing to DynamoDB, which allows us to replay messages if the job state API is temporarily unavailable.

Why CloudFront Functions instead of Lambda@Edge? CloudFront Functions are synchronous and execute in under 1ms, making them ideal for simple rewrites. Lambda@Edge has access to request body and more complex logic, but adds 10-50ms of latency. Since we only need path-based routing and CORS injection, Functions