Delivery Statuses

POST /messages and POST /omnimessages return 202 Accepted as soon as the platform takes the message. Everything that happens afterwards is reported through two complementary surfaces:

  1. Webhooks (push, recommended) — the platform calls your HTTPS endpoint with a messageStatus event on each state change.
  2. EDRs (pull) — Event Detail Records, the per-message delivery log queryable via GET /edrs by message id or by time range.

Message statuses (MessageStatus)

The status field in messageStatus webhook events takes exactly these values:

Status Meaning
sent Successfully handed to the channel/operator.
delivered The operator or app confirmed delivery to the recipient.
displayed The user opened the message (channels with read receipts: RCS, WhatsApp, Viber). Plain SMS does not report this state.
failed The channel rejected the send.
undeliverable The operator could not deliver (e.g. unreachable subscriber).
unknown The channel did not report a final state.

Lifecycle

stateDiagram-v2
    [*] --> accepted: 202 from POST /messages
    accepted --> sent: handed to channel
    sent --> delivered: confirmed by operator/app
    delivered --> displayed: user opened (read-receipt channels)
    sent --> failed: channel rejected
    sent --> undeliverable: operator could not deliver
    delivered --> [*]
    displayed --> [*]
    failed --> [*]
    undeliverable --> [*]

For an SMS the typical happy path is accepted → sent → delivered. The successOn parameter (sent / delivered / seen) tells the platform which of these states counts as success for a step inside an omnimessage cascade.

Operator-level statuses in EDRs (mdnStatus)

Each EDR row also carries mdnStatus — the delivery status in the operator’s own wording, alongside the boolean lifecycle flags isSent / isDelivered / isSeen:

mdnStatus Meaning
ENROUTE Accepted by the SMSC and still in transit.
DELIVRD Delivered to the handset.
EXPIRED The delivery window expired before the message reached the handset.
DELETED The message was deleted.
UNDELIV Not delivered — invalid number or unreachable subscriber.
ACCEPTD Accepted by the SMSC.
UNKNOWN Status is unknown.
REJECTD Rejected by the SMSC (for example no route). This is not an API-key or scope error — those return HTTP 401.
IM_EXPD Instant-message expiry.

How the two levels relate

  • The webhook status is the platform-normalized state — one vocabulary across all channels.
  • mdnStatus is the raw operator verdict recorded on the EDR — use it (together with errorMessage) to diagnose why a message ended in undeliverable or failed.
  • A delivered webhook corresponds to an EDR with isDelivered: true and mdnStatus: "DELIVRD"; negative outcomes surface as UNDELIV, EXPIRED, or REJECTD with the details in errorMessage.

Choosing push vs pull

Webhook GET /edrs
Latency Real time On demand
Best for Production status tracking Reconciliation, audits, cost reporting
Requirements Public HTTPS endpoint, idempotent on messageId (duplicates possible after retries) None
Limits — Time-range mode requires timeFrom (Alaris start_date). timeTo is optional; if omitted, the window is one hour after the start. Page size is capped at 100

Webhook target resolution: the per-request webhook field wins, then the Sender’s webhookUrl, then the webhook on the API key. If none is set, status events are not delivered anywhere and GET /edrs is the only way to retrieve them.

A practical polling pattern when you cannot host a webhook: query GET /edrs by messageId, stop polling a message once isDelivered/isSeen is true or mdnStatus shows a final negative state, and use time-range queries (with offset/limit paging) for periodic reconciliation.

What an EDR row contains

messageId, channelType, senderId, destination number, country/countryCode/network (operator routing), body (masked when the send used isOtp: true), your clientInfo tag, rate and finalCost (see Balance & pricing), isSent/isDelivered/isSeen, mdnStatus, errorMessage, and the timestamps time, timeSent, timeDelivered, timeSeen.

For cascades: pass the per-step messageIds to GET /edrs — there is no lookup by omniTransactionId, so keep the step IDs from the POST /omnimessages response.

See the API Reference: GET /edrs, POST /messages, POST /omnimessages.