Skip to main content
Version: Latest

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

FieldTypeDescription
eventTypestringAlways message
timestampstringEvent timestamp in ISO 8601 UTC
expiresAtstringOptional session expiry in ISO 8601 UTC
payload.providerstringwhatsapp, instagram, messenger, telegram, sms or web_chatt2me
payload.directionstringIN or OUT
payload.message.idstringHub message UUID
payload.message.sessionIdstringSession UUID; always present and created on the first inbound or outbound when needed
payload.message.typestringNormalized message type
payload.message.contentobjectNormalized message content
payload.sender.idstringSender routing identifier
payload.recipient.idstringRecipient routing identifier
payload.recipient.applicationChannelIdstringApplication-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.

FieldTypeDescription
eventTypestringAlways MessageStatus
messageIdstringID of the message (returned when you sent it)
statusstringNew message status
timestampnumberUnix timestamp of the status event
channelstringChannel where the message was sent

Possible statuses

StatusDescription
sentSent to the channel server
deliveredDelivered to the recipient's device
readRead by the recipient (when available in the channel)
failedDelivery 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",
"email": "[email protected]"
},
"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:

Node.js — Auto-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?' },
}),
});
}
});
Respond 200 before processing

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