Skip to main content

Webhooks for WhatsApp Calls and Message Reactions [NEW]

Four new webhook events

TimelinesAI now emits four additional webhook events that you can subscribe to from your workspace's webhook settings:

  • call:incoming:missed — fires when an incoming WhatsApp call ends without being answered.

  • call:incoming:ended — fires when an incoming WhatsApp call ends after being answered or declined.

  • call:outgoing:ended — fires when an outgoing WhatsApp call ends (answered, no-answer, or rejected by recipient).

  • message:reaction — fires when an emoji reaction is added or removed on any synced message (text, file, voice, call) in direct or group chats.

Subscribe via the Workspace → Integrations → Webhooks page or the Public API webhook endpoints.

Heads up: Each workspace can register up to 10 webhook endpoints. Reach out if that's tight for your use case — we're actively gathering feedback on multi-event subscriptions.

When call webhooks fire

  • Only after a call ends — never on call start.

  • Calls in "Ringing" status do not fire any webhook. Once the call moves to a final status (Missed, Answered, Rejected, Ended, No Answer), the appropriate event fires.

  • Scheduled calls do not fire any call webhook on their own — only when an actual call happens.

  • Group-chat calls do fire webhooks (unlike CRM sync, which skips them).

New shared fields on call + reaction events

The new events introduce three fields that are also part of message:reaction and may be added to other events in the future:

Field

Block

Value

chat.chat_type

chat

direct or group. Replaces the old is_group boolean — is_group is not present in the new payloads.

chat.platform

chat

whatsapp today; future values may include waba.

whatsapp_account.whatsapp_account_wid

whatsapp_account

The WhatsApp WID, e.g. [email protected].

Call event payload

{
"event_type": "call:incoming:missed",
"chat": {
"full_name": "Agent Smith",
"chat_url": "https://app.timelines.ai/chat/123456/messages/",
"chat_id": 123456,
"chat_type": "direct",
"platform": "whatsapp",
"phone": "+123456789",
"responsible_name": "Agent Brown",
"responsible_email": "[email protected]"
},
"whatsapp_account": {
"whatsapp_account_wid": "[email protected]",
"full_name": "Agent Brown",
"email": "[email protected]",
"phone": "+123456789"
},
"call_details": {
"duration": null,
"direction": "incoming",
"is_video": false,
"status": "Missed",
"timestamp": "2026-05-24 10:35:18 +0200",
"message_uid": "c7ec509d-0171-1ead-a84b-c6943a644768",
"caller": {
"full_name": "John Smith",
"phone": "+123456789"
},
"recipient": {
"full_name": "Agent Brown",
"phone": "+987654321"
}
}
}

call_details.status values:

Direction

Status values

Incoming (call:incoming:missed)

Missed

Incoming (call:incoming:ended)

Answered, Rejected

Outgoing (call:outgoing:ended)

Ended, No Answer, Rejected

Note on status wording. Webhook payloads use Rejected when a call was declined by the recipient (both for incoming declined by your WhatsApp device, and for outgoing rejected by the remote contact). The text shown on the Pipedrive / HubSpot activity stream for the same call may still say Declined — they refer to the same event.

call_details.duration is in seconds when available; null otherwise. It is always present in the payload (never omitted).

message:reaction payload

{
"event_type": "message:reaction",
"chat": {
"full_name": "Agent Smith",
"chat_url": "https://app.timelines.ai/chat/123456/messages/",
"chat_id": 123456,
"chat_type": "direct",
"platform": "whatsapp",
"phone": "+123456789",
"responsible_name": "Agent Brown",
"responsible_email": "[email protected]"
},
"whatsapp_account": {
"whatsapp_account_wid": "[email protected]",
"full_name": "Agent Brown",
"email": "[email protected]",
"phone": "+15559876543"
},
"reaction": {
"message_uid": "a5bbb005-37f2-402c-96fa-e479a2e09b02",
"action": "set",
"emoji": "👍",
"reactor": {
"full_name": "John Doe",
"phone": "+15551234567"
},
"timestamp": "2026-05-24 12:00:00 +0200",
"reactions": {
"👍": "2",
"❤️": "1"
}
}
}

reaction.action is set when a reaction was added, clear when removed. reaction.emoji is the added or removed emoji. reaction.reactions is the current aggregate emoji → count map after this event (same format as MessageInfo.reactions on the Public API). reaction.reactor is null if attribution isn't available at dispatch time.

A few things worth knowing about message:reaction:

  • One event fires per reaction — no batching. A group chat with 10 people reacting to the same message will produce 10 webhook events. We may add optional aggregation later; tell us if you'd benefit.

  • Fires for reactions on any message type — text, file/media, voice, call.

  • Fires for reactions added or cleared in WhatsApp itself or in TimelinesAI's Shared Inbox.

  • If a reaction is added to a message that wasn't synced into TimelinesAI, the reaction is skipped (no event).

Did this answer your question?