Skip to content

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

Status Description Final?
pending Queued, waiting to send No
sent Submitted to carrier network No
DELIVRD Delivered to recipient’s handset Yes
UNDELIV Delivery failed (invalid number, phone off) Yes
REJECTD Rejected by carrier (filtered, blacklisted) Yes
EXPIRED Delivery timed out Yes
UNKNOWN Final status unknown Yes
failed Rejected by the gateway before routing (usually no route available), or an internal queue failure. Never charged — the portal reports these as Not Billed. Yes
pending → sent → DELIVRD success
pending → sent → UNDELIV bad number / phone off
pending → sent → REJECTD carrier blocked
pending → sent → EXPIRED recipient unreachable
pending → failed no route available / internal error (never charged)
Place Field
Message status message.status
Message lists messages[].status (+ filter aliases like delivered)
Delivery webhook status
Statistics Aggregated into delivered / undelivered / pending / expired / not_billed

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.