Webhooks

Event types

Subscribe to the events you need. Each one carries what a workflow needs, without a second call.

All events

Messages

message.received
A customer sent a message
message.sent
Meta accepted an outbound message, from the inbox or the API
message.status_updated
An outbound message moved to sent, delivered, read, failed or rejected

Conversations

conversation.created
A customer's first message opened a conversation
conversation.assigned
The assigned member or team changed
conversation.status_changed
The status or snooze changed
note.created
Someone added an internal note

Contacts

contact.created
A contact was created
contact.updated
Fields changed, listed in changed
contact.deleted
A contact was deleted
contact.restored
A deleted contact came back
contact.blocked
A contact was blocked
contact.unblocked
A contact was unblocked
contact.lifecycle_changed
A contact moved to another stage
contact.tag_added
A tag was put on a contact
contact.tag_removed
A tag was taken off a contact
contact.merged
A contact was merged into another

Other

import.completed
An import finished or failed
ping
A test sent from Settings

The contact

Message, conversation, note and contact events carry the contact in data.contact, the same object Get a contact returns. Every key is present, null when empty. It is read when the event is sent, so it can be newer than the event.

data.contact
{
  "customer_id": "4829173650",
  "first_name": "Ama",
  "last_name": null,
  "username": null,
  "phone_e164": "+237690000001",
  "email": null,
  "language": "fr",
  "lifecycle": { "stage_id": "new", "name": "New", "color": "#9CA3AF" },
  "tags": [],
  "collaborators": [],
  "attributes": { "plan": "gold" },
  "status": "active",
  "channel_id": "0e8d3c1f-7a6b-4b52-9e34-2d1f5c8a9b70",
  "created_at": "2026-10-05T14:02:11Z",
  "updated_at": "2026-10-05T14:02:11Z"
}

Contact events also keep the contact fields at the top of data, as before.

Files

message.received and message.sent carry media for an image, audio, video, document or sticker, and null otherwise.

data.media
{
  "id": "7d1e4a0c-3b52-4c8e-9f60-2a5b8c1d9e33",
  "mime_type": "image/jpeg",
  "size": 48213,
  "filename": null,
  "source": "omnio",
  "url": "https://...",
  "url_expires_at": "2026-10-05T14:17:11Z"
}
  • url needs no token and expires after 15 minutes. Download the file when the event arrives. Each retry gets a new link.
  • For a fresh link later, call Get a file link with the id.
  • A file sent by link has source "link", its original url and url_expires_at null.
  • An inbound file is copied a few seconds after it arrives and the event waits up to a minute for it. If it is not ready, url is null and the link endpoint answers once it is.

Message status

A status never goes back. When a customer reads a message, each earlier unread one gets its own event with inferred set to true.

message.status_updated · data
{
  "message_id": "3c5e7a90-8d1b-4e26-a4f7-5b9d0c2e1a68",
  "seq": 43,
  "conversation_id": "b1c7e0a4-52a3-4a3c-8d0e-6c1b9e5f7a20",
  "channel_id": "0e8d3c1f-7a6b-4b52-9e34-2d1f5c8a9b70",
  "customer_id": "4829173650",
  "message_type": "text",
  "status": "read",
  "previous_status": "delivered",
  "inferred": false,
  "read_at": "2026-10-05T14:05:39Z",
  "error": null
}