Workers & Background Processing
The asynchronous machinery - send, delivery receipts, two-way SMS, streams, and webhooks.
Almost everything interesting in Outpost happens asynchronously. The API responds in milliseconds; five workers, connected by queues, do the actual work.
How they relate
Each worker is a pure function processing queue messages. In production they run as Lambda functions triggered by SQS; for local development, long-running daemons poll the same queues and invoke the same handlers, so behavior matches production.
The five workers
Send worker
Takes queued messages and submits them to AWS End User Messaging. Before every send it runs preflight checks - does the phone exist, is it opted out, are attempts left, is another attempt still in flight? - and settles the message as FAILED if a permanent check fails. The in-flight check is the exception: a duplicate send is dropped without settling, since the pending delivery receipt decides the outcome. Provider errors are classified as retryable (queue-level retry) or fatal. See Messages.
DLR worker
Processes delivery receipts (DLRs) - the carrier network's verdict on each send, arriving via SNS/SQS up to 72 hours later. It records an event for every receipt and updates the attempt with cost and carrier data on final receipts, then decides the message's fate: settle it (as DELIVERED, BLOCKED, or FAILED once attempts are exhausted or the message has become permanently unsendable, such as an opt-out arriving mid-flight), or requeue for retry. A failed-but-retryable receipt fires ATTEMPT_FAILED and requeues without ever setting the message's status to FAILED; only the final, no-more-retries outcome writes FAILED and fires the terminal FAILED event.
Two-way worker
Processes inbound SMS. Every inbound message is recorded (direction INBOUND). Keyword replies drive opt-out state:
- STOP (and
UNSUBSCRIBE,CANCEL,END,QUIT,STOPALL) - opts the phone out, optionally sending a confirmation first. - START (
UNSTOP,YES) - opts the phone back in with a confirmation. - HELP (
INFO) - sends usage instructions.
Confirmation messages bypass opt-out checks (they must be sendable while the phone is still opted out) and never receive compliance footers.
Stream worker
Listens to DynamoDB streams - every write to any entity flows through it. It routes changes to the owning entity's logic, which turns them into stats increments and webhook fan-out. Idempotency layers ensure redelivered stream records never double-count.
Webhook worker
Executes one webhook delivery attempt at a time: loads the event, builds the payload with fresh message/phone context, POSTs to the configured endpoint, and records the outcome on the event.
Infrastructure notes
- Phone pools are shared infrastructure - provisioned outside application environments (number registration is slow) and supplied via Vault. Pool country coverage drives the compliance LINK footer decision - see Messages.
- Delivery receipts are environment-owned - each environment has its own configuration set and DLR queue, so receipts route back to the environment that sent the message.
- Two-way topics are pool-level - environments sharing a pool all receive the same inbound STOP, so only one environment should send confirmations.