API reference
Webhook events
The events OmniMessage sends to your webhook endpoints, with an example payload for each. Every payload is an event object whose data.object is the resource concerned.
Delivery#
Each event is sent as a POST request with a JSON body and the headers below. Answer with any 2xx status within 10 seconds. Failed deliveries are retried up to eight times over about 45 hours. The webhooks guide covers setup, signature verification and retries in full.
| Header | Description |
|---|---|
OmniMessage-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" keyed with the endpoint secret>. |
OmniMessage-Event-Id | Same as id in the body. |
OmniMessage-Event-Type | Same as type in the body. |
User-Agent | Always OmniMessage-Webhooks/1.0. |
OmniMessage-Verification-Token | The verification_token of the endpoint, when it has one. For receivers that cannot verify the signature. |
message.sent#
Message sent
The provider accepted an outbound message. data.object is the message with status: "sent".
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "message.sent",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "msg_2b1Xw9aQ3rT8yU0pL4kZ",
"object": "message",
"mode": "live",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"direction": "outbound",
"to": "+971501234567",
"from": "+971800123456",
"type": "text",
"content": {
"text": {
"body": "Your code is 482910"
}
},
"status": "sent",
"error": null,
"reference": "order-1042",
"metadata": {
"user_id": "u_17"
},
"billing": {
"source": "package",
"amount_micros": 0,
"package_grant_id": "grant_5tGh2Kp9LmQ4xW7nB1cD",
"refunded": false
},
"sender": null,
"contact_id": null,
"created_at": "2026-10-05T09:30:00.000Z",
"updated_at": "2026-10-05T09:30:02.871Z",
"sent_at": "2026-10-05T09:30:01.210Z",
"delivered_at": null,
"read_at": null,
"failed_at": null
}
}
}message.delivered#
Message delivered
An outbound message reached the recipient device. Not every channel reports delivery; SMS routes may stop at sent.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "message.delivered",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "msg_2b1Xw9aQ3rT8yU0pL4kZ",
"object": "message",
"mode": "live",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"direction": "outbound",
"to": "+971501234567",
"from": "+971800123456",
"type": "text",
"content": {
"text": {
"body": "Your code is 482910"
}
},
"status": "delivered",
"error": null,
"reference": "order-1042",
"metadata": {
"user_id": "u_17"
},
"billing": {
"source": "package",
"amount_micros": 0,
"package_grant_id": "grant_5tGh2Kp9LmQ4xW7nB1cD",
"refunded": false
},
"sender": null,
"contact_id": null,
"created_at": "2026-10-05T09:30:00.000Z",
"updated_at": "2026-10-05T09:30:02.871Z",
"sent_at": "2026-10-05T09:30:01.210Z",
"delivered_at": "2026-10-05T09:30:02.871Z",
"read_at": null,
"failed_at": null
}
}
}message.read#
Message read
The recipient opened an outbound message. Sent only on channels that report read receipts and when the recipient has them enabled.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "message.read",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "msg_2b1Xw9aQ3rT8yU0pL4kZ",
"object": "message",
"mode": "live",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"direction": "outbound",
"to": "+971501234567",
"from": "+971800123456",
"type": "text",
"content": {
"text": {
"body": "Your code is 482910"
}
},
"status": "read",
"error": null,
"reference": "order-1042",
"metadata": {
"user_id": "u_17"
},
"billing": {
"source": "package",
"amount_micros": 0,
"package_grant_id": "grant_5tGh2Kp9LmQ4xW7nB1cD",
"refunded": false
},
"sender": null,
"contact_id": null,
"created_at": "2026-10-05T09:30:00.000Z",
"updated_at": "2026-10-05T09:30:02.871Z",
"sent_at": "2026-10-05T09:30:01.210Z",
"delivered_at": "2026-10-05T09:30:02.871Z",
"read_at": "2026-10-05T09:31:15.402Z",
"failed_at": null
}
}
}message.failed#
Message failed
An outbound message could not be delivered. data.object.error explains why. The charge has already been refunded: billing.refunded is true.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "message.failed",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "msg_9cV4nH7jK2mP5qR8sT1w",
"object": "message",
"mode": "live",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"direction": "outbound",
"to": "+971501234567",
"from": "+971800123456",
"type": "text",
"content": {
"text": {
"body": "Your code is 482910"
}
},
"status": "failed",
"error": {
"code": "provider_error",
"message": "Message undeliverable.",
"provider_code": "131026"
},
"reference": "order-1042",
"metadata": {
"user_id": "u_17"
},
"billing": {
"source": "wallet",
"amount_micros": 1000,
"package_grant_id": null,
"refunded": true
},
"sender": null,
"contact_id": null,
"created_at": "2026-10-05T09:30:00.000Z",
"updated_at": "2026-10-05T09:30:02.871Z",
"sent_at": null,
"delivered_at": null,
"read_at": null,
"failed_at": "2026-10-05T09:30:03.118Z"
}
}
}message.received#
Message received
A message arrived on one of your channels. data.object is a message with direction: "inbound" and status: "received". Inbound messages are free.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "message.received",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "msg_4fD8sA1gH6jK9lZ3xC5v",
"object": "message",
"mode": "live",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"direction": "inbound",
"to": "+971800123456",
"from": "+971501234567",
"type": "text",
"content": {
"text": {
"body": "Where is my order?"
}
},
"status": "received",
"error": null,
"reference": null,
"metadata": {},
"billing": {
"source": "none",
"amount_micros": 0,
"package_grant_id": null,
"refunded": false
},
"sender": {
"name": "Layla Hassan",
"username": null
},
"contact_id": "ct_8Jk3mP6qR9sT2vW5xY1z",
"created_at": "2026-10-05T09:42:10.000Z",
"updated_at": "2026-10-05T09:42:10.000Z",
"sent_at": null,
"delivered_at": null,
"read_at": null,
"failed_at": null
}
}
}channel.connected#
Channel connected
A channel reached connection_status: "connected", after being created or after a reconnect.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "channel.connected",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"object": "channel",
"mode": "live",
"type": "whatsapp",
"name": "Support line",
"identifier": "+971800123456",
"status": "active",
"connection_status": "connected",
"capabilities": [
"text",
"attachments",
"template",
"button",
"list",
"cta_url",
"location",
"contacts",
"flow",
"product",
"product_list",
"catalog",
"carousel",
"location_request"
],
"created_at": "2026-10-01T08:00:00.000Z"
}
}
}channel.disconnected#
Channel disconnected
A channel lost its link to the provider, for example because a token was revoked. Sending on it fails with channel_not_connected until it is reconnected.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "channel.disconnected",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"object": "channel",
"mode": "live",
"type": "whatsapp",
"name": "Support line",
"identifier": "+971800123456",
"status": "active",
"connection_status": "disconnected",
"capabilities": [
"text",
"attachments",
"template",
"button",
"list",
"cta_url",
"location",
"contacts",
"flow",
"product",
"product_list",
"catalog",
"carousel",
"location_request"
],
"created_at": "2026-10-01T08:00:00.000Z"
}
}
}balance.low#
Balance low
The wallet dropped below the low-balance threshold set in the console and no package credits remain. Sent at most once every 24 hours. data.object is the balance.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "balance.low",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"object": "balance",
"currency": "USD",
"wallet_micros": 1870000,
"packages": [],
"credits_remaining": 0
}
}
}package.exhausted#
Package exhausted
The last credit of a package was consumed. Further messages draw on the next package or the wallet. data.object is the package.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "package.exhausted",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "grant_5tGh2Kp9LmQ4xW7nB1cD",
"name": "100K messages",
"quota": 100000,
"remaining": 0,
"channel_types": null,
"expires_at": "2027-10-05T00:00:00.000Z"
}
}
}package.expiring#
Package expiring
A package with unused credits expires within 7 days. Sent once per package. data.object is the package.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "package.expiring",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "grant_5tGh2Kp9LmQ4xW7nB1cD",
"name": "100K messages",
"quota": 100000,
"remaining": 81234,
"channel_types": null,
"expires_at": "2026-10-12T00:00:00.000Z"
}
}
}campaign.started#
Campaign started
A campaign began sending: its audience snapshot is complete, or it was resumed after a pause. data.object is the campaign with status: "sending".
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "campaign.started",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "cmp_5Cf8hK1mP4rT7vY0aD3g",
"object": "campaign",
"mode": "live",
"name": "October delivery update",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"status": "sending",
"pause_reason": null,
"failure_reason": null,
"audience": {
"type": "list",
"list_id": "lst_4Bd7fH0jL3nQ6sU9wZ2c"
},
"message": {
"type": "template",
"template": {
"name": "order_update",
"language": {
"code": "en"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "{{first_name}}"
},
{
"type": "text",
"text": "on its way"
}
]
}
]
}
},
"merge_fallbacks": {
"first_name": "there"
},
"require_opt_in": false,
"consent_declared": true,
"schedule": {
"send_at": null,
"timezone": "Asia/Dubai",
"send_window": {
"start": "09:00",
"end": "20:00"
},
"recipient_timezone": true
},
"throttle_per_second": 20,
"counters": {
"total": 1280,
"queued": 640,
"sent": 598,
"delivered": 571,
"read": 302,
"failed": 9,
"skipped": 33,
"cancelled": 0,
"skip_reasons": {
"unsubscribed": 21,
"no_identifier": 12
}
},
"cost": {
"currency": "USD",
"estimated_wallet_micros": 247000,
"estimated_credits": 1000,
"wallet_micros": 0,
"package_credits": 598
},
"created_at": "2026-10-05T09:00:00.000Z",
"launched_at": "2026-10-05T09:05:00.000Z",
"started_at": "2026-10-05T09:05:02.000Z",
"paused_at": null,
"completed_at": null,
"cancelled_at": null
}
}
}campaign.paused#
Campaign paused
A campaign stopped sending. data.object.pause_reason says why: user, admin, channel_unavailable, or insufficient_balance when the funds ran out (top up, then resume).
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "campaign.paused",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "cmp_5Cf8hK1mP4rT7vY0aD3g",
"object": "campaign",
"mode": "live",
"name": "October delivery update",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"status": "paused",
"pause_reason": "user",
"failure_reason": null,
"audience": {
"type": "list",
"list_id": "lst_4Bd7fH0jL3nQ6sU9wZ2c"
},
"message": {
"type": "template",
"template": {
"name": "order_update",
"language": {
"code": "en"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "{{first_name}}"
},
{
"type": "text",
"text": "on its way"
}
]
}
]
}
},
"merge_fallbacks": {
"first_name": "there"
},
"require_opt_in": false,
"consent_declared": true,
"schedule": {
"send_at": null,
"timezone": "Asia/Dubai",
"send_window": {
"start": "09:00",
"end": "20:00"
},
"recipient_timezone": true
},
"throttle_per_second": 20,
"counters": {
"total": 1280,
"queued": 640,
"sent": 598,
"delivered": 571,
"read": 302,
"failed": 9,
"skipped": 33,
"cancelled": 0,
"skip_reasons": {
"unsubscribed": 21,
"no_identifier": 12
}
},
"cost": {
"currency": "USD",
"estimated_wallet_micros": 247000,
"estimated_credits": 1000,
"wallet_micros": 0,
"package_credits": 598
},
"created_at": "2026-10-05T09:00:00.000Z",
"launched_at": "2026-10-05T09:05:00.000Z",
"started_at": "2026-10-05T09:05:02.000Z",
"paused_at": "2026-10-05T09:12:30.000Z",
"completed_at": null,
"cancelled_at": null
}
}
}campaign.completed#
Campaign completed
Every recipient of a campaign was processed. Delivery and read counters can still rise afterwards, as receipts arrive.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "campaign.completed",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "cmp_5Cf8hK1mP4rT7vY0aD3g",
"object": "campaign",
"mode": "live",
"name": "October delivery update",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"status": "completed",
"pause_reason": null,
"failure_reason": null,
"audience": {
"type": "list",
"list_id": "lst_4Bd7fH0jL3nQ6sU9wZ2c"
},
"message": {
"type": "template",
"template": {
"name": "order_update",
"language": {
"code": "en"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "{{first_name}}"
},
{
"type": "text",
"text": "on its way"
}
]
}
]
}
},
"merge_fallbacks": {
"first_name": "there"
},
"require_opt_in": false,
"consent_declared": true,
"schedule": {
"send_at": null,
"timezone": "Asia/Dubai",
"send_window": {
"start": "09:00",
"end": "20:00"
},
"recipient_timezone": true
},
"throttle_per_second": 20,
"counters": {
"total": 1280,
"queued": 0,
"sent": 1229,
"delivered": 1190,
"read": 742,
"failed": 18,
"skipped": 33,
"cancelled": 0,
"skip_reasons": {
"unsubscribed": 21,
"no_identifier": 12
}
},
"cost": {
"currency": "USD",
"estimated_wallet_micros": 247000,
"estimated_credits": 1000,
"wallet_micros": 229000,
"package_credits": 1000
},
"created_at": "2026-10-05T09:00:00.000Z",
"launched_at": "2026-10-05T09:05:00.000Z",
"started_at": "2026-10-05T09:05:02.000Z",
"paused_at": null,
"completed_at": "2026-10-05T09:07:10.000Z",
"cancelled_at": null
}
}
}campaign.failed#
Campaign failed
A campaign could not continue, for example because its channel was deleted. data.object.failure_reason explains why. Recipients not sent to yet are closed as cancelled.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "campaign.failed",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "cmp_5Cf8hK1mP4rT7vY0aD3g",
"object": "campaign",
"mode": "live",
"name": "October delivery update",
"channel_id": "ch_7Hq2mN5vB8cX1zL0pK3j",
"channel_type": "whatsapp",
"status": "failed",
"pause_reason": null,
"failure_reason": "The channel of this campaign was deleted.",
"audience": {
"type": "list",
"list_id": "lst_4Bd7fH0jL3nQ6sU9wZ2c"
},
"message": {
"type": "template",
"template": {
"name": "order_update",
"language": {
"code": "en"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "{{first_name}}"
},
{
"type": "text",
"text": "on its way"
}
]
}
]
}
},
"merge_fallbacks": {
"first_name": "there"
},
"require_opt_in": false,
"consent_declared": true,
"schedule": {
"send_at": null,
"timezone": "Asia/Dubai",
"send_window": {
"start": "09:00",
"end": "20:00"
},
"recipient_timezone": true
},
"throttle_per_second": 20,
"counters": {
"total": 0,
"queued": 0,
"sent": 0,
"delivered": 0,
"read": 0,
"failed": 0,
"skipped": 0,
"cancelled": 0,
"skip_reasons": {}
},
"cost": {
"currency": "USD",
"estimated_wallet_micros": 247000,
"estimated_credits": 1000,
"wallet_micros": 0,
"package_credits": 0
},
"created_at": "2026-10-05T09:00:00.000Z",
"launched_at": "2026-10-05T09:05:00.000Z",
"started_at": null,
"paused_at": null,
"completed_at": null,
"cancelled_at": null
}
}
}webhook.test#
Test event
Sent only when you call POST /v1/webhook_endpoints/{id}/test or press "Send test event" in the console. It is delivered whatever the endpoint subscriptions are. data.object is the webhook endpoint.
{
"id": "evt_6hJ9kL2zX5cV8bN1mQ4w",
"object": "event",
"type": "webhook.test",
"mode": "live",
"created_at": "2026-10-05T09:30:02.900Z",
"data": {
"object": {
"id": "we_3kL9pQ2wE5rT8yU1iO4a",
"object": "webhook_endpoint",
"mode": "live",
"url": "https://example.com/hooks/omni",
"description": "Production delivery receipts",
"events": [
"message.delivered",
"message.failed",
"message.received"
],
"status": "active",
"filters": {},
"metadata": {},
"source": null,
"created_by": {
"type": "api_key",
"id": "key_1qW4eR7tY0uI3oP6aS9d"
},
"has_verification_token": false,
"created_at": "2026-10-02T11:15:00.000Z"
}
}
}