Messages
Message status updated
message.status.updatedFires when the delivery status of an outbound message changes. Statuses progress in order: queued → sent → delivered → read → played; a lower status is silently dropped if a higher one is already recorded. failed is a terminal status, but it is only recorded when the message has not already reached a higher status: a provider failure receipt arriving AFTER delivered/read/played was already recorded (e.g. delayed or duplicated) is silently suppressed — an already-delivered message's status is never downgraded retroactively. A suppressed receipt is not lost silently: it is written to the channel log (GET /channels/{id}/logs) under the message.status.failed_suppressed code. failed is also sent when the provider rejects the send outright (for example, Meta refuses a template's parameters) or the send could never be confirmed: such a message never gets an external_message_id, and error_details carries the provider's own error text and code. This event fires for outbound messages only, and not every channel reports every status (e.g. some channels do not report read).
HTTP request
AISAR sends a signed POST request to your webhook_url:
POST {webhook_url}
Content-Type: application/json
X-AISAR-Signature: sha256=...
{
"event": "message.status.updated",
"event_id": "evt_01JXXXXXXXXXXXXXXXXXXXXX",
"timestamp": "2026-03-26T12:00:00.000000Z",
"company_id": 1,
"data": {
"message_id": 123,
"conversation_id": 45,
"channel_id": 2,
"external_message_id": "3EB0ABC123456789",
"status": {
"name": "delivered",
"timestamp": "2026-03-26T12:00:05+00:00",
"error_details": null
}
}
}The X-AISAR-Signature header carries an HMAC-SHA256 signature of the request body — use it to verify the request came from AISAR.
Example payload
{
"event": "message.status.updated",
"event_id": "evt_01JXXXXXXXXXXXXXXXXXXXXX",
"timestamp": "2026-03-26T12:00:00.000000Z",
"company_id": 1,
"data": {
"message_id": 123,
"conversation_id": 45,
"channel_id": 2,
"external_message_id": "3EB0ABC123456789",
"status": {
"name": "delivered",
"timestamp": "2026-03-26T12:00:05+00:00",
"error_details": null
}
}
}Payload fields
| Field | Type | Description |
|---|---|---|
message_id | integer | Internal message ID |
conversation_id | integer | Conversation ID |
channel_id | integer | Channel ID |
external_message_id | string|null | Platform-specific message ID; null when the provider rejected the send before accepting it |
status.name | string | Status: queued (queued for sending), sent (sent to recipient device), delivered (delivered to recipient device), read (read by recipient), played (played by recipient — voice/video), failed (delivery failed, terminal, but suppressed when a higher status is already recorded — see the event description) |
status.timestamp | string | ISO 8601 timestamp when the status was recorded |
status.error_details | string|null | Error description in the "text (code)" format — a human-readable reason (from the runtime/provider response or an error catalog) with the technical code in parentheses; the code may be absent, leaving just the text. Only present when name is failed |