Outpost logoOutpost Docs

Messages

The core entity - one SMS tracked from creation through delivery, failure, or block.

A message is the core entity in Outpost: a single SMS with full lifecycle tracking. Every message records its recipient, body, status, attempt history, cost breakdown, and settlement state.

In the dashboard: Sending & Tracking Messages shows how to send and trace messages visually.

How it relates

Messages sit at the bottom of the grouping hierarchy and are the join point of the whole system: they carry the ownership stamp (tenant/project), the grouping IDs (communication/campaign), and the recipient (phone).

The lifecycle

A message crosses three system boundaries: the API (validation, creation, queuing), the send worker (submission to AWS), and the DLR worker (delivery receipts from the carrier network).

Statuses

A message is settled when it reaches a terminal outcome: DELIVERED, FAILED, or BLOCKED. FAILED is terminal-only: it is written exactly once, the moment a message settles as failed. A failed-but-retryable delivery attempt never sets the message's status to FAILED; the status stays SENT/CARRIER_ROUTING until the retry flips it back to QUEUED. Settlement is final - it triggers cost recording, stats aggregation, and webhook delivery, and a settled message can never be re-queued. BLOCKED (carrier spam filters, opt-out) is never retried.

Sending: what the API does with your request

POST /messages/sms accepts 1–100 items per request. Each item runs through a validation pipeline:

  1. Phone formatting - the number is normalized to E.164 and a phone record is created if new.
  2. Deduplication - if the request has a communicationId and this phone already received a message for it, the item is rejected. See Communications & Campaigns.
  3. Phone validation - landlines and invalid numbers are rejected.
  4. Opt-out enforcement - opted-out phones are rejected.
  5. Create & queue - the message is created and queued for the send worker.

The endpoint returns 202 with per-item outcomes: accepted, rejected (business-rule violations, each with a machine-readable reason like OPTED_OUT or DUPLICATE), and errors. One bad recipient never fails the rest of the batch.

Items can carry an opaque metadata bag (up to 20 keys) that Outpost never interprets but echoes back on webhook deliveries - use it to correlate deliveries with your own records.

Retries

Outpost retries generously to maximize delivery, up to 3 attempts per message by default:

  • Transient provider errors (throttling, timeouts) retry via the queue automatically - the message's status stays QUEUED while this happens.
  • Carrier failures reported in a delivery receipt re-queue the message if attempts remain. This fires an ATTEMPT_FAILED event, not FAILED - the message isn't done yet.
  • Stale attempts - if no delivery receipt arrives within 72 hours, the attempt is force-failed so the message can retry or settle instead of hanging forever.
  • Manual retry - POST /messages/:messageId/retry (also in the dashboard) creates a brand-new message with the same content.

When attempts are exhausted (or a failure isn't retryable at all), the message settles: status becomes FAILED, and a FAILED event fires alongside the specific reason event (e.g. MAX_ATTEMPTS_REACHED). Every attempt is recorded in the message's attemptHistory with its own status, provider IDs, carrier info, and cost - see Cost Model.

Compliance: footers and sender identity

Outpost automatically appends opt-out footers to satisfy TCPA/CTIA guidelines:

  • LINK footer (Opt out: <url>) - added to every message sent to a country not covered by dedicated numbers, where STOP replies cannot be received. Not disableable - it's the only working opt-out path for those recipients.
  • REPLY footer (Reply STOP to unsubscribe.) - a periodic reminder (28-day cadence per phone) for covered countries, controlled by the sending project's compliance settings.

A project may also enable a sender identity prefix ({identity}: {body}) applied to every outbound message.

The tenant-supplied body is never modified; the exact assembled text delivered to the recipient is stored separately as sentBody - essential in a compliance dispute. Note that footers and prefixes count toward SMS segment limits, so they can increase cost.

Inbound messages

Messages have a direction: OUTBOUND or INBOUND. Inbound messages (replies, STOP/START/HELP keywords) are created by the two-way worker with status DELIVERED.

API reference

Calling from TypeScript? See Generating a Typed Client.

On this page