Event Types and Payload
When an event occurs, Hub Chatt2.me makes a POST to your endpoint with a JSON payload. This page describes the payload structure for each event type.
message event
Fired for inbound and outbound messages on WhatsApp, Instagram, Messenger, Telegram, SMS and Web chatt2.me. Hub Chatt2.me creates or reuses the session before the first inbound or outbound message webhook.
General payload fields
| Field | Type | Description |
|---|---|---|
eventType | string | Always message |
timestamp | string | Event timestamp in ISO 8601 UTC |
expiresAt | string | Optional session expiry in ISO 8601 UTC |
payload.provider | string | whatsapp, instagram, messenger, telegram, sms or web_chatt2me |
payload.direction | string | IN or OUT |
payload.message.id | string | Hub message UUID |
payload.message.sessionId | string | Session UUID; always present and created on the first inbound or outbound when needed |
payload.message.type | string | Normalized message type |
payload.message.content | object | Normalized message content |
payload.sender.id | string | Sender routing identifier |
payload.recipient.id | string | Recipient routing identifier |
payload.recipient.applicationChannelId | string | Application-channel UUID, when applicable |
Example — Text message received
{
"eventType": "message",
"timestamp": "2026-09-08T14:14:00.000Z",
"payload": {
"provider": "whatsapp",
"direction": "IN",
"message": {
"id": "abc123de-f456-789a-bcde-f01234567890",
"sessionId": "7a4b7555-50fa-421e-8eba-d34ac97066b9",
"type": "text",
"content": {
"text": "Hello, I'd like to know more about your products"
}
},
"sender": {
"id": "5511993986082"
},
"recipient": {
"id": "5511912345678",
"applicationChannelId": "6cbd6789-1d2b-47e0-9391-5eb91d2839ea"
}
}
}
payload.message.sessionId is also present for Sandbox messages, using the
free Sandbox session identifier.
Creating a session does not itself charge a channel conversation. Billable outbound messages are forwarded only after the applicable charge can be fully funded. Provider echoes that cannot be funded are retained for audit but are not forwarded to customer webhooks. A valid AI result finalizes its AI service charge even if delivery later fails; conversation and channel outbound charges remain tied to delivery.
MessageStatus event
Fired when the status of a message you sent is updated.
| Field | Type | Description |
|---|---|---|
eventType | string | Always MessageStatus |
messageId | string | ID of the message (returned when you sent it) |
status | string | New message status |
timestamp | number | Unix timestamp of the status event |
channel | string | Channel where the message was sent |
Possible statuses
| Status | Description |
|---|---|
sent | Sent to the channel server |
delivered | Delivered to the recipient's device |
read | Read by the recipient (when available in the channel) |
failed | Delivery failed |
Example — Message delivered
{
"eventType": "MessageStatus",
"messageId": "9307b9ab-5fab-4151-b7c2-b00c2a8a6e8e",
"status": "delivered",
"timestamp": 1700000060,
"channel": "whatsapp"
}
Example — Message read
{
"eventType": "MessageStatus",
"messageId": "9307b9ab-5fab-4151-b7c2-b00c2a8a6e8e",
"status": "read",
"timestamp": 1700000120,
"channel": "whatsapp"
}
Agent operational events
flag_for_review, session_transfer, and end_chat use this envelope:
{
"eventType": "session_transfer",
"timestamp": "2026-08-19T13:06:34.806Z",
"expiresAt": "2026-08-20T13:14:34.906Z",
"payload": {
"provider": "web_chatt2me",
"direction": "IN",
"message": {
"id": "f3e1c5d0-4b8a-4e2b-9f3c-1a2b3c4d5e6f",
"sessionId": "7a4b7555-50fa-421e-8eba-d34ac97066b9",
"queue": { "id": "28fb06bc-ce0d-4e3b-8446-50bd47b25818", "name": "Financeiro" },
"reason": "requested_by_visitor",
"summary": "Visitor requested financial support."
},
"sender": {
"id": "visitor-id",
"name": "Fernando",
"username": "Fernando",
"phoneNumber": "5511999999999",
},
"recipient": { "id": "chatt2me", "applicationChannelId": "35c32d6a-cc9a-4b1d-8950-682802767cc5" }
}
}
payload.message.id is a newly generated Hub message ID and can be used to
correlate logs and retries. expiresAt is present when the session has an
expiry. flag_for_review contains sessionId and summary; end_chat adds
reason and summary; session_transfer can also include queue.
For Web Chatt inbound messages and Web Chatt operational events, sender can
also include optional phoneNumber (digits without +) and email. These
values are visitor-provided and unverified; they are not identity or routing
keys. Continue to use sender.id when replying to the visitor. Other channel
providers and Web Chatt outbound recipients do not include these contact fields.
Replying to the user from the webhook
When receiving a message event, use the payload fields to build the reply:
app.post('/webhooks/chatt2me', async (req, res) => {
res.status(200).send('OK'); // Respond 200 first!
const { eventType, payload } = req.body;
if (
eventType === 'message' &&
payload.direction === 'IN' &&
payload.message.content?.text === 'hi'
) {
await fetch('https://app.chatt2.me/v1/communication/whatsapp/message', {
method: 'POST',
headers: {
'x-api-key': process.env.CHATT2ME_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: payload.recipient.applicationChannelId,
to: payload.sender.id,
content: { text: 'Hello! How can I help you?' },
}),
});
}
});
Send 200 OK immediately upon receiving the webhook and process the logic afterwards (or in the background). This prevents timeouts and false errors in the webhook log.
Next steps
- Inspecting logs — view all received events and identify failures
- Retrying failures — resend events that failed