How Outpost Fits Together
Every component of Outpost and how they relate to each other.
Outpost is built from a small set of components. This page is the system map: what each component is, and - more importantly - how they relate.
The two hierarchies
Outpost organizes data along two independent hierarchies that meet at the message.
The ownership hierarchy answers "who is sending?" Tenants own projects; projects own API keys and webhook configs. Every authenticated request is scoped by this chain.
The grouping hierarchy answers "what is being sent?" Campaigns group communications; communications group messages; each message records its individual send attempts.
The Phone sits outside both hierarchies. Phones are global - the same +15551234567 is a single record shared by every tenant that messages it, because opt-out is a regulatory commitment to the phone's owner, not to any one tenant.
The full entity map
How to read this:
- A Message is the center of the system. It belongs to a tenant/project (ownership), optionally to a communication/campaign (grouping), and always targets a phone.
- Events are the immutable audit trail. Message events track the message lifecycle; phone events track phone state changes (opt-outs, validation failures).
- Webhooks are how events leave Outpost. A project's webhook configs subscribe to event types; matching events produce delivery attempts.
- Stats are derived counters, updated asynchronously when messages settle or phones change state. They are a read model - messages, phones, and events remain the source of truth.
How a message flows through the system
The runtime picture: components that do work, connected by queues.
- The API validates each recipient (format, duplicates, validity, opt-out), creates the message, and queues it.
- The send worker runs preflight checks and submits to AWS End User Messaging.
- AWS sends back delivery receipts; the DLR worker updates the message, settles it or requeues it for retry.
- The two-way worker handles inbound replies - STOP/START/HELP keywords drive opt-out state on the phone.
- Every DynamoDB write flows through streams to the stream worker, which updates stats and fans out webhook deliveries.
Workers and queues are covered in detail in Workers & Background Processing.
Component index
| Component | One-line summary |
|---|---|
| Tenants & Projects | Ownership and configuration: who sends, with what settings |
| API Keys | Authentication; admin keys vs tenant-scoped keys |
| Messages | One SMS with full lifecycle tracking; the core entity |
| Phones & Opt-Out | Global phonebook, validation, regulatory opt-out |
| Communications & Campaigns | Optional grouping layers; deduplication and rollups |
| Events | Immutable audit trail for messages and phones |
| Webhooks | Push event notifications to downstream systems |
| Stats | Pre-aggregated counters powering dashboards |
| Workers | The async machinery: send, DLR, two-way, stream, webhook |
| Cost Model | How per-attempt carrier costs roll up |