Serverless pipeline that receives emails and forwards them as SMS messages.
Some services only send notifications by email — there is no push notification, no SMS option, and no API. If you want to receive those notifications on your phone while away from a screen, you have to build that bridge yourself.
Email to SMS is a serverless pipeline that receives emails at a custom domain and forwards them as SMS messages. Incoming emails are stored in S3 via an SES receipt rule, which triggers a Lambda function. The Lambda parses the email, matches the sender against a configured allowlist, and delivers an SMS to each configured recipient via the Twilio API.
The initial use case is Garmin LiveTrack: when a tracked athlete starts an activity, Garmin sends a notification email. This pipeline intercepts that email and delivers it as a text message within seconds, without requiring the recipient to have a Garmin account or the Garmin Connect app installed.
Adding support for a new email sender requires implementing a single parser interface and registering it by key — no changes to the core handler or infrastructure. Recipient phone numbers and Twilio credentials live in SSM Parameter Store and can be updated without a redeploy.
If this kind of integration could solve a problem you're working on, we'd be happy to talk through it. Use the contact form at the bottom of this page to get in touch.
Routes inbound SMTP traffic to SES via an MX record pointing to the SES inbound SMTP endpoint for the deployment region. Also holds the DKIM CNAME records used by SES to verify domain ownership and sign outbound messages.
Matches emails addressed to the ingestion domain and stores them to an S3 bucket. Spam scanning is enabled. SES receipt rules filter by recipient domain only — sender filtering is handled in the Lambda.
Stores each raw email as an object under the emails/ prefix, named by the SES message ID. A 7-day lifecycle rule expires objects automatically, providing a debug artifact window without unbounded storage growth. An S3 event notification triggers the Lambda on every new object.
The core of the pipeline. Reads the raw email from S3, parses it with mailparser, matches the sender (From address) and relay target (To address) against the configured rule set, invokes the appropriate parser, and delivers an SMS to each recipient via the Twilio SDK. Written in TypeScript and bundled with esbuild; AWS SDK packages are excluded since they are provided by the Lambda Node.js runtime.
Holds the pipeline configuration as a JSON document: Twilio credentials, and a list of rules each specifying a relay origin, relay target, parser type, and SMS recipient phone numbers. Sensitive values stay out of the repository and can be updated without a redeploy. The Lambda reads config once per container instance and caches it in memory; changes take effect on the next cold start.
Delivers SMS messages to recipient phone numbers via the Twilio REST API. Messages are sent as transactional SMS from a provisioned toll-free origination number for consistent sender identity. Twilio credentials are stored in SSM Parameter Store and never in code.
Each email type has a dedicated parser class that implements the EmailParser interface. Parsers are registered by string key in a central registry. The current registry includes a Garmin LiveTrack parser that extracts the athlete name and LiveTrack URL from the notification email body using cheerio.
SES can invoke Lambda directly, but routing through S3 first provides a 7-day debug artifact — the raw email — without any extra effort. It also decouples ingestion from processing: a Lambda failure or cold start delay does not affect email receipt, and emails can be reprocessed by replaying the S3 event.
Recipient phone numbers and Twilio credentials are sensitive and change independently of the codebase. Storing them in SSM Parameter Store keeps them out of the repository and allows live updates without a redeploy. The Lambda reads config once per container and caches it in memory; changes take effect on the next cold start.
SNS Direct SMS requires provisioning a toll-free origination number through AWS, completing carrier registration, and verifying each recipient individually in the SNS sandbox before sending live messages — a multi-week process with limited debugging visibility. Twilio provides equivalent transactional SMS delivery with a mature SDK, a richer console for inspecting message logs, and no per-recipient sandbox verification requirement.
SES receipt rules filter by recipient domain only — they cannot filter by sender address. The Lambda applies the allowlist immediately after parsing the email, matching on both the sender address (relay origin) and the destination address (relay target). Unknown combinations are dropped without invoking any parser or making any Twilio API calls.
Multiple users of the same service share the same sending address, so the sender alone cannot distinguish them. Each user configures their service to send notifications to a unique address at the relay domain. SES accepts all mail at the domain level, so no infrastructure change is needed per user — only a new config entry specifying the relay target and the desired SMS recipients.
Each email type has a dedicated parser class registered by string key. Adding support for a new sender requires implementing the EmailParser interface and adding one entry to the registry — no changes to the main handler or infrastructure.
The project started with AWS SAM but migrated to Terraform before the first production deployment. SAM's Events: Type: S3 creates a circular CloudFormation dependency when the S3 bucket is defined in the same template. Terraform's explicit depends_on chain resolves this cleanly and also manages aws_ses_active_receipt_rule_set natively, eliminating a manual post-deploy activation step.
Runtime dependencies (mailparser, cheerio, twilio) are bundled into the deployment zip. @aws-sdk/* packages are excluded because they are provided by the Lambda Node.js runtime. This keeps the bundle small and cold start times low.