Building a Multi-Carrier SMS Relay for QuickDumpNow: Twilio Integration & CloudFront Tracking
What Was Done
We solved a carrier-level limitation for QuickDumpNow's (QDN) customer notification pipeline by implementing a Twilio-based SMS relay. The original architecture attempted to cascade notifications across multiple carriers (primary line → Sergio's main line → backup carrier contact), but the underlying carrier infrastructure doesn't support chained forwarding at scale. This post covers the infrastructure decisions, Lambda integration patterns, and API Gateway wiring that now enable flexible message routing.
Additionally, we deployed a real-time job tracking page (/track) on the main QDN site to give customers visibility into their service orders—a critical feature for a same-day waste removal business.
Technical Details: Twilio Account Setup & Credential Management
Twilio credentials were provisioned and stored securely in the shared secrets file:
- Location:
/Users/cb/Documents/repos/.secrets/repos.env(permissions: 600) - What's stored: Account SID (for authentication), Auth Token (for admin operations), and API Key + Secret pair (for SDK runtime calls)
- Why the split: The API key pair is preferred for SDK runtime because it's narrower in scope and can be rotated independently; the auth token remains for admin operations like webhook configuration or number provisioning
The reference file at /Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/reference_twilio_credentials.md documents which credential type to use in each context—future sessions can consult this without re-asking.
Critical security decision: We did not commit credentials to the repo. All Twilio setup is environment-injected at Lambda runtime via the repos.env secrets file, which is sourced during CloudFormation stack initialization.
Infrastructure: Lambda & API Gateway Wiring
The core integration lives in the QDN data-CRUD Lambda:
- File:
/Users/cb/Documents/repos/sites/dashboard.quickdumpnow.com/lambda/lambda_function.py - Primary function: Handles job state machine transitions, maintenance data persistence, and now message dispatch
We added four new API Gateway routes to the QDN stack:
POST /api/jobs/{jobId}/messages — Enqueue SMS via Twilio
GET /api/jobs/{jobId}/messages — Retrieve message history for a job
POST /api/maintenance — Seed/update maintenance schedules
OPTIONS * — CORS preflight for browser clients
Each route includes CORS headers (Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers) to permit the dashboard frontend to communicate across the domain boundary.
Why this structure: Keeping messages at the job level (rather than contact level) lets us track the full audit trail of customer communications tied to a service order. When a technician updates job status, the Lambda can automatically trigger notifications through the same pipeline.
Tracking Page Deployment
We deployed a customer-facing job tracker at /track on the main QDN site:
- Source file:
/Users/cb/Documents/repos/sites/quickdumpnow.com/track/index.html - S3 bucket:
quickdumpnow.com(CloudFront distribution ID:E[distribution-id]) - Rewrite logic: CloudFront function deployed to handle
/trackrouting
The page accepts a jobToken query parameter, looks up the corresponding job in the Lambda API, and displays:
- Current job status (e.g., "Scheduled," "In Progress," "Complete")
- Technician assignment & estimated arrival window
- Message history (all notifications sent to the customer)
- Next steps (e.g., "Payment due," "Rate your service")
Token validation happens client-side (via API call) to avoid exposing full job records to unauthenticated users—the API returns only data associated with that specific token.
State Machine & Message Dispatch
The Lambda implements a job state machine with automatic notifications:
Job States:
CREATED → ASSIGNED → IN_PROGRESS → COMPLETE → RATED
Notifications trigger on:
CREATED: "Your job is confirmed. ID: [jobId]"
ASSIGNED: "Your tech [name] is on the way. ETA: [window]"
IN_PROGRESS: "Service underway. Your tech just arrived."
COMPLETE: "Job done! Rate your experience: [link]"
RATED: "Thank you for rating. Here's your receipt: [link]"
The append_message() helper function was updated across two call sites to use the new Twilio SDK binding. Each message includes:
- Timestamp (when the Lambda executed)
- Message text
- Delivery status (pending, sent, failed)
- Carrier response (SID, if Twilio accepted it)
Maintenance Data Seeding
We deployed an initial maintenance schedule to S3 as /tmp/maintenance_seed.json, diffed it against the live state, and pushed the result via API to:
- S3 object:
s3://quickdumpnow.com/data/maintenance.json - Access method: Lambda reads on every request; CloudFront caches for 5 minutes (TTL configurable)
This seeding includes truck service intervals, equipment rotation schedules, and fuel/supply procurement windows—data that drives the maintenance dashboard UI and triggers internal alerts when thresholds approach.
Why This Architecture?
Asynchronous message delivery: Twilio calls happen in the Lambda's execution context, but we don't block the job state transition on delivery. If Twilio is slow, the customer's job still updates. A separate SQS queue (future enhancement) could decouple this further.
Token-based job lookup: Rather than requiring login to check status, customers receive a private token in their SMS. This lowers friction and works offline (they can share the link with family members without revealing other jobs).
CloudFront function for rewrites: The /track path routing is handled by a CloudFront function (not S3 routing rules) because it allows us to inspect the request, validate the token signature, and conditionally serve the page or a 404. This happens at the edge, so rejection is fast.
Deployment Checklist
- ✅ Twilio credentials stored in repos.env with correct file permissions
- ✅ Four new API Gateway routes registered and tested anonymously
- ✅ Lambda package updated with Twilio SDK and message dispatch logic
- ✅ CloudFront function deployed and attached to QDN distribution default behavior
- ✅
/trackpage deployed to S3 and cache invalidated