Skip to content

Every message moves through a lifecycle of statuses, reported by carriers via delivery reports (DLR).

StatusDescriptionFinal?
pendingQueued, waiting to sendNo
sentSubmitted to carrier networkNo
DELIVRDDelivered to recipient’s handsetYes
UNDELIVDelivery failed (invalid number, phone off)Yes
REJECTDRejected by carrier (filtered, blacklisted)Yes
EXPIREDDelivery timed outYes
UNKNOWNFinal status unknownYes
failedInternal error (queue/config failure)Yes
pending → sent → DELIVRD success
pending → sent → UNDELIV bad number / phone off
pending → sent → REJECTD carrier blocked
pending → sent → EXPIRED recipient unreachable
pending → failed internal error
PlaceField
Message statusmessage.status
Message listsmessages[].status (+ filter aliases like delivered)
Delivery webhookstatus
StatisticsAggregated into delivered / undelivered / pending / expired

Messages stuck in sent/ENROUTE without a final DLR for 3+ days are automatically moved to UNDELIV by an hourly reconciliation job — so long-running integrations never see permanently “pending” messages.

  • DELIVRD is the only confirmed handset delivery. sent means the carrier accepted the message, not that the phone received it.
  • UNDELIV vs REJECTD: UNDELIV is usually a recipient problem (dead number, phone off for days); REJECTD is a carrier policy decision (content filtering, blacklist) — review your content/sender if it spikes.
  • EXPIRED often means the handset was off or out of coverage for the entire validity period.
  • Clean your lists: repeatedly UNDELIV numbers should be suppressed — they cost money and hurt your sending reputation.