Message status update event (message.status.updated)

Dispatched via HTTP POST to the configured webhook URL whenever an outbound SMS message transitions delivery status (moving into the sent or notsent folder, or updating its delivery/view state).

POST <client_webhook_url>

Table of contents

Callback summary

Property Value
Event type "message.status.updated"
HTTP method POST
Target endpoint Per-message webhook.url (if specified when sending) or the user account's registered webhook url (configured via POST /api/v2/webhook or PUT /api/v2/webhook)
Content-Type application/json
Signature header X-Hub-Signature-256: sha256=<hex_digest> (when an account webhook secret is configured)
Expected client response Any 2xx HTTP status code (e.g., 200 OK, 204 No Content); no response payload is required

Important: Webhook requests are always formatted as a JSON array of event envelopes (WebhookEventEnvelope[]), even when delivering a single event (an array of length 1, up to 100 events per delivery request). If the webhook endpoint receives multiple event types, a single batch array may contain both "message.received" and "message.status.updated" events. Receiving endpoints must deserialize the root payload as an array and inspect each item's type discriminator.

Request headers (sent to client)

Header Presence Description
Content-Type Always application/json
X-Hub-Signature-256 When secret is configured Lowercase hexadecimal HMAC-SHA256 signature of the raw UTF-8 request body, prefixed with sha256= (e.g., sha256=a4c8e1f2...), computed using the account webhook's shared secret (whsec_...). Omitted if dispatching to a per-message webhook.url when no account-level webhook secret is configured.

Webhook payload schema

Root array (WebhookDeliveryPayload)

Type Description
object[] (WebhookEventEnvelope[]) Non-empty JSON array of 1 to 100 event envelope objects.

Event envelope object (WebhookEventEnvelope)

Field Type Description
eventId string (UUID) Unique identifier for this webhook event. Retained across retry attempts; use this value for idempotency and deduplication.
time string (ISO 8601) Timestamp when the event was generated (with timezone offset).
type string Event type discriminator. Set to "message.status.updated" for delivery status update events.
user string Username of the account that dispatched the outbound message.
payload object (MessageStatusUpdatedPayload) Event-specific data object containing the delivery status transition details.

payload object (MessageStatusUpdatedPayload)

Field Type Presence Description
messageId string (UUID) Always Unique identifier of the outbound message.
folder string Always Target message folder after the status transition: "sent" or "notsent".
status string Always Updated delivery status state ("rejected", "submitted", "partiallysubmitted", "submitfailed", "delivered", "partiallydelivered", "deliveryfailed", "viewed").
statusDescription string Always Human-readable explanation or provider status report message corresponding to status (e.g., "Delivered", "Submit failed").
updatedAt string (ISO 8601) Always Timestamp when the status transition occurred.

Message status values & target folders

Status Target folder Description
"rejected" "notsent" Message was rejected prior to submission due to validation or policy rules.
"submitfailed" "notsent" Transmission attempt to the telecom provider/SMSC failed.
"submitted" "sent" Message was successfully submitted to the telecom provider/SMSC.
"partiallysubmitted" "sent" Multi-part message was partially submitted to the provider network.
"delivered" "sent" Message was confirmed delivered to the recipient handset.
"partiallydelivered" "sent" Multi-part message was partially confirmed delivered.
"deliveryfailed" "sent" Handset delivery failed (e.g., unreachable, expired, or invalid subscriber).
"viewed" "sent" Recipient read or viewed the message payload (where supported).

Delivery, retry & signature verification

1. Batching & retry policy

  • Batch size: Up to 100 queued events destined for the same webhook URL are batched into a single HTTP POST request and delivered sequentially in FIFO order.
  • Retries & backoff: If the client endpoint does not return a 2xx HTTP status code (or if the connection times out), delivery is retried up to 20 times using quadratic backoff:

    delay = base + (base × retry2) seconds, where base = 60 seconds.

  • Idempotency: Retried deliveries reuse the original eventId. Receiving servers should track processed eventId values to avoid duplicate processing.

2. Verifying X-Hub-Signature-256

  1. Read the X-Hub-Signature-256 header and verify that it starts with sha256=.
  2. Capture the raw UTF-8 request body bytes before parsing or re-serializing JSON.
  3. Compute the HMAC-SHA256 hash over the raw body bytes using the UTF-8 bytes of the shared secret (whsec_...) returned when the account webhook was configured.
  4. Format the hash as lowercase hexadecimal prefixed with sha256= and compare it against the header value using a constant-time comparison function (e.g., CryptographicOperations.FixedTimeEquals).

Examples

1. Message delivered status update (status: "delivered")

Single message.status.updated event delivered when an outbound SMS is confirmed delivered to the recipient handset.

Webhook request (sent to client):

POST /api/sms/inbound HTTP/1.1
Host: client.example.com
Content-Type: application/json
X-Hub-Signature-256: sha256=a4c8e1f2b3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c
[
  {
    "eventId": "91a27b9c-53e3-4d43-9831-29e1ebba5122",
    "time": "2026-10-21T10:21:00+02:00",
    "type": "message.status.updated",
    "user": "http_user",
    "payload": {
      "messageId": "26385eed-6a6c-4deb-b549-dc055211a71d",
      "folder": "sent",
      "status": "delivered",
      "statusDescription": "Delivered",
      "updatedAt": "2026-10-21T10:21:00+02:00"
    }
  }
]

Expected client response (2xx):

HTTP/1.1 200 OK

(No response payload is required)

2. Message delivery failed status update (status: "deliveryfailed")

Single message.status.updated event delivered when handset delivery fails after submission.

Webhook request (sent to client):

POST /api/sms/inbound HTTP/1.1
Host: client.example.com
Content-Type: application/json
X-Hub-Signature-256: sha256=b5d9f2a3c4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d
[
  {
    "eventId": "4f5a6b7c-8d9e-4f0a-9b1c-2d3e4f5a6b7c",
    "time": "2026-10-21T10:22:00+02:00",
    "type": "message.status.updated",
    "user": "http_user",
    "payload": {
      "messageId": "8f3b2a1c-9d4e-4f5a-b6c7-1a2b3c4d5e6f",
      "folder": "sent",
      "status": "deliveryfailed",
      "statusDescription": "Delivery failed",
      "updatedAt": "2026-10-21T10:22:00+02:00"
    }
  }
]

3. Batch delivery of multiple status updates

Multiple status transition events batched into a single HTTP POST request.

Webhook request (sent to client):

POST /api/sms/inbound HTTP/1.1
Host: client.example.com
Content-Type: application/json
X-Hub-Signature-256: sha256=c6e0a3b4d5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e
[
  {
    "eventId": "91a27b9c-53e3-4d43-9831-29e1ebba5122",
    "time": "2026-10-21T10:21:00+02:00",
    "type": "message.status.updated",
    "user": "http_user",
    "payload": {
      "messageId": "26385eed-6a6c-4deb-b549-dc055211a71d",
      "folder": "sent",
      "status": "delivered",
      "statusDescription": "Delivered",
      "updatedAt": "2026-10-21T10:21:00+02:00"
    }
  },
  {
    "eventId": "d34e6f8a-72b1-4c55-9012-34f5a6b7c8d9",
    "time": "2026-10-21T10:21:02+02:00",
    "type": "message.status.updated",
    "user": "http_user",
    "payload": {
      "messageId": "9e4c3b2d-0e5f-4a6b-c7d8-2b3c4d5e6f7a",
      "folder": "notsent",
      "status": "submitfailed",
      "statusDescription": "Submit failed",
      "updatedAt": "2026-10-21T10:21:02+02:00"
    }
  }
]

Client response requirements

Client HTTP status code Dispatcher behavior
2xx (200 OK – 299) Delivery is marked as acknowledged and removed from the dispatch queue. No response payload is required (any response body is ignored).
3xx, 4xx, 5xx, or Network Timeout Delivery is treated as failed and scheduled for retry according to the quadratic backoff policy (up to 20 retries).

More information