Skip to main content

Event Overview

Three events cover every message that passes through a conversation. They share one payload shape, so a single handler works for all of them. Method: POST
Content-Type: application/json
The split between message_sent and template_sent is deliberate: a campaign integration usually wants template sends without every conversational reply alongside them. A template fires template_sent only — it does not also fire message_sent.
These are opt-in. Enable them under Dashboard → Webhook (account-wide) or in a chat agent’s Webhook panel. See Introduction.

Payload Structure

Field Descriptions

Use contact_number rather than branching on direction to work out which of from/to is the customer — it is already resolved for you.

About status

status here is the state at the moment the message was storedsent for an accepted outbound message, failed for a rejected one, and read for anything inbound (you are reading it, by definition). It is not the final state of an outbound message. To follow a message to delivered or read, subscribe to message_status_updated.
A failed send still fires message_sent or template_sent, with status set to the failure and msg_id empty. That is intentional — a rejected send is something you want to know about, not something that silently disappears. Check status before treating a message_sent as a success.

What never fires

  • Internal recordsdeveloper notes, chat-reset markers and link-click records are not messages anyone sent
  • Test conversations — the Test Chat tab does not deliver real messages, so it does not fire real webhooks
  • call_ended on a chat agent’s webhook — see Introduction

Example Implementation

Node.js (Express)

Python (FastAPI)

Delivery Status

Follow a message to delivered and read

Get WhatsApp Messages

Pull conversation history over the API instead