Arrow
Get started with Wazzup
Arrow
How to connect a messenger
Arrow
How to use Wazzup chats
Arrow
How to pay for the service
Arrow
Bitrix24
Arrow
Kommo
Arrow
Zoho CRM
Arrow
HubSpot
Arrow
Pipedrive
Arrow
Other CRMs
Arrow
How to sell even easier
Arrow
All about WABA
Arrow
For partners
Arrow
Public API
For partners
Arrow

Webhooks

Webhooks let you receive real‑time notifications about events happening in Wazzup — for example, when a message status changes to "Delivered," or when a channel becomes "Unauthorized" and needs to be reconnected.

Wazzup sends webhooks to your HTTP endpoint. You need to subscribe to webhooks for a specific end‑customer account using the client_access_token in the header: Authorization: Bearer <client_access_token>

Minimum requirements for receiving webhooks: Respond with HTTP 200 as quickly as possible. Logging and business logic can be handled asynchronously.

Supported Events

  • message.add — an incoming message from a customer has been received;
  • message.status_update — the status of an outgoing message has changed (e.g., from "Delivered" to "Read");
  • message.delete — a message has been deleted;
  • message.edit — a message has been edited. This event is only sent for messages on channels that support editing;
  • waba_template.status_update — the moderation status of a WABA template has changed, indicating whether the template is ready to use;
  • channel.status_update — the status of a channel has changed;
  • channel.create — a channel has been created;
  • channel.delete — a channel has been deleted;
  • channel.qr_update — the QR code or connection code has been updated — the new code must be displayed to the user. Sent for WhatsApp, Telegram Personal, and Viber;
  • channel.waba_tier_update — the WABA tier has changed. The tier determines how many conversations can be initiated within a 24‑hour period;
  • group_chat.memberships_update — the membership of a WhatsApp group chat has changed, or a participant has been renamed;
  • group_chat.update — group chat information has been updated;
  • crm_entities.contact_add — a new contact needs to be created;
  • crm_entities.deal_add — a new deal needs to be created.

Subscribe only to the events that your system actually processes.

Subscribing to Webhooks

You can subscribe to multiple events in a single request.

Method POST /v2/webhooks
Body parameter. Required are marked with * Parameter type Parameter description
data* object List of webhooks you are subscribing to
data.url* string The URL where you want to receive webhooks
data.event* string The webhook event

Webhooks for creating a contact or a deal have specific characteristics:

You can subscribe to both the crm_entities.contact_add and crm_entities.deal_add webhooks, or only to crm_entities.contact_add. The webhook for creating a deal will not work without subscribing to the webhook for creating a new contact.

Request example:

curl -L 'https://tech.wazzup24.com/v2/webhooks' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <client_access_token>' \
-d '{
"data": [
{
"url": "https://webhook.site/73796fab-2e6d-492a-b68e-061d825b7db5",
"event": "message.add"
},
{
"url": "https://webhook.site/73796fab-2e6d-492a-b68e-061d825b7db5",
"event": "message.status_update"
}
]
}'

Response example:

{
"data": [
{
"id": "d6c04-7623-43ae-a024-7b36c4004c13",
"url": "https://webhook.site/73796fab-2e6d-492a-b68e-061d825b7db5",
"event": "message.add"
},
{
"id": "b0a26-10d9-4d47-8cda-892c9b471a05",
"url": "https://webhook.site/73796fab-2e6d-492a-b68e-061d825b7db5",
"event": "message.status_update"
}
],
"meta": {
"timestamp": 1759494767
}
}

Result: A subscription has been created for the message.add and message.status_update events. Webhooks will be sent to https://webhook.site/73796fab-2e6d-492a-b68e-061d825b7db5.

The response includes a subscription id, which is required to manage webhooks using other methods.

Modifying Webhook Subscription

This method allows you to subscribe to additional webhooks, cancel an existing subscription, or change the URL where events are received.

Method PATCH /v2/webhooks
Body parameter. Required are marked with * Parameter type Parameter description
data* object List of webhooks you are subscribing to
data.id* string Subscription ID
data.url* string The URL where you want to receive webhooks
data.event* string The webhook event

Request Example:

curl -L -X PATCH 'https://tech.wazzup24.com/v2/webhooks' \
-H 'Authorization: Bearer <client_access_token>' \
-H 'Content-Type: application/json' -d '{
"data": [
{
"id": "42cdd-a0a6-40fd-b55b-040e7c605d0a",
"url": "https://webhook.site/1d2c6a79-d709-4d00-ad24-856e3d8edfd5",
"event": "message.delete"
}
]
}'

Response example:

{
"data": [
{
"id": "d6c04-7623-43ae-a024-7b36c4004c13",
"url": "https://webhook.site/new-webhook-url",
"event": "message.delete"
}
],
"meta": {
"timestamp": 1759483585
}
}

Result: The webhook URL has been updated for subscription id: 411d6c04-7623-43ae-a024-7b36c4004c13.

Retrieving a List of Webhook Subscriptions

Method GET /v2/webhooks

Request Example:

curl -L 'https://tech.wazzup24.com/v2/webhooks' -H 'Authorization: Bearer <client_access_token>'

Response example:

{
"data": [
{
"id": "891cdbf4-07bd-4c24-942b-27d5c820c879",
"url": "https://webhook.site/3d76c4a6-f1ab-4090-a766-3f9a07bca714",
"event": "channel.status_update"
},
{
"id": "1e35de57-ca04-45ba-a345-67f0d8b11138",
"url": "https://webhook.site/3d76c4a6-f1ab-4090-a766-3f9a07bca714",
"event": "channel.create"
},
{
"id": "ccbe0af3-2f12-4e6e-8c35-78fe3c9d3714",
"url": "https://webhook.site/3d76c4a6-f1ab-4090-a766-3f9a07bca714",
"event": "channel.update"
}
],
"meta": {
"timestamp": 1759483435
}
}

Result: A list of all webhook subscriptions for the end‑customer account.

Deleting a Webhook Subscription

Method DELETE /v2/webhooks
Body parameter Parameter type Parameter description
data* object List of webhooks you are subscribing to
data.id* string Subscription ID

Request Example:

curl -L -X DELETE 'https://tech.wazzup24.com/v2/webhooks' \
-H 'Authorization: Bearer <client_access_token>' \
-H 'Content-Type: application/json' \
-d '{
"data": [
{
"id": "dbf4-07bd-4c24-942b-27d5c820c879"
}
]
}'

Response example:

{
"data": null,
"meta": {
"timestamp": 1759483702
}
}

Result: Subscription id: dbf4-07bd-4c24-942b-27d5c820c879 has been deleted. You will no longer receive webhooks for this event.

Webhook Structure

{
"event": "message.add" | "message.status_update" | "message.delete" | "message.edit" | "channel.status_update" | "channel.delete" | "channel.create" | "channel.qr_update" | "channel.waba_tier_update" | "waba_template.status_update" | "group_chat.memberships_update" | "group_chat.update",
"data": [],
"meta": {
"idempotency_key": UUIDv4,
"timestamp": Unix timestamp in seconds
}
}

The data field contains the event itself. Event structures:

message.add

{
"message_id": "a14325c0-97c0-43dd-917e-f0ad7460b6a9",
"channel_id": "a14325c0-97c0-43dd-917e-f0ad7460b6a9",
"direction": "inbound",
"quoted_message_id": "90126ace-fc10-44ed-90de-c30c3802fcf5" | null,
"timestamp": 1762340622177,
"crm_user_id": "123" | null,
"status": "sent",
"recipient": {
"chat_id": "3719999999",
"username": "my_tg_user",
"phone": "37198511122",
"chat_type": "telegram",
"name": "Tg User"
},
"text": "Hello!",
"attachment": {
"url": "https://some.url/photo.jpg",
"name": "photo.jpg",
"mimetype": "image/jpeg",
"size": 14832,
"sha1": "123456abcdf"
},
"template": {
"id": "2b1c3d4e-5f67-890a-bcde-f123456789ab",
"values": [ "Hello" ],
"buttons": [
{ "text": "Yes", "payload": "yes" },
{ "text": "No", "payload": "no" }
]
}
}
{
  "event": "message.add",
  "data": [
    {
      "message_id": "a5e8ba61-bc6c-41fe-9598-db7a9cbfbcbd",
      "chat_id": "221601332",
      "channel_id": "5f3f029a-8e76-4203-9434-fd490f8db848",
      "direction": "inbound",
      "quoted_message_id": null,
      "timestamp": 1776953557938,
      "crm_user_id": null,
      "text": "Hello",
      "status": "accepted",
      "sender": {
        "chat_type": "telegram",
        "chat_id": "221601332",
        "name": "Test Testovich",
        "username": "testaccount",
        "phone": 79111234567
      },
      "recipient": {
        "chat_id": "221601332",
        "chat_type": "telegroup",
        "avatar": null,
        "username": null,
        "phone": null,
        "name": "Group chat",
        "profile_id": null,
        "advert": null
      }
    }
  ],
  "meta": {
    "idempotency_key": "9ef07b18-e95f-4ba1-9981-63d95d8ed468",
    "timestamp": 1776953558
  }
}
message.add
│
├── message_id *
├── channel_id *
├── direction *
├── quoted_message_id
├── timestamp *
├── status *
├── crm_user_id
├── sender
│ ├── chat_type *
│ ├── chat_id *
│ ├── name
│ ├── username
│ └── phone
├── recipient *
│ ├── chat_type *
│ ├── chat_id
│ ├── username
│ ├── phone
│ ├── profile_id *
│ └── advert *
├── text
├── attachment
│ ├── url *
│ ├── name
│ ├── mimetype
│ ├── size
│ └── sha1
├── template
│ ├── id *
│ ├── values[]
│ └── buttons[]
│ ├── text *
│ └── payload *
└── referral
├── source_url
├── source_type
├── source_id
├── headline
├── body
├── media_type
├── image_url
├── video_url
├── thumbnail_url
└── ctwa_clid
Parameter. Required are marked with * Type Description
message_id* string Message ID
channel_id* string Wazzup channel ID
direction* string Message direction: inbound, outbound
quoted_message_id string ID of the quoted message
timestamp* number Unix timestamp
status* string Message status (accepted)
crm_user_id string ID of the CRM user or "null"
sender object sender Message sender in a WhatsApp, Telegram, or MAX group chat
recipient* object recipient

The chat where a new outgoing or incoming message has appeared.

If the webhook is about a new message in a dialogue, this object contains information about the contact the user is communicating with.

If the webhook is about a new message in a group chat, this object contains general information about the group without specifying the participants.

text string Message text. One of text, attachment, or template must be present
attachment object attachment Attachment object
template object template WABA template object
referral object referral Only for WABA. Marketing data from ads. This data is passed when a customer clicks a button in an ad and opens WhatsApp. It helps you understand where the customer learned about you

sender (object)

Parameter. Required are marked with * Type Description
chat_type* string

Chat type. Available values:

  • whatsapp — WhatsApp chat,
  • telegram — Telegram chat,
  • max — MAX chat.
chat_id* string

Message sender ID — the contact's messenger account ID

name string

Sender's name

username string Telegram only. Telegram username, without the @ symbol at the beginning
phone string Sender's phone number in international format, if known. Without + or any other characters

recipient (object)

Parameter. Required are marked with * Type Description
chat_type* string

Chat type. Supported values:

  • whatsapp — individual WhatsApp chats,
  • whatsgroup — WhatsApp group chats,
  • viber — Viber chats,
  • instagram — Instagram chats,
  • telegram — individual Telegram chats,
  • telegroup — Telegram group chats,
  • vk — VK chats,
  • avito — Avito chats,
  • max — individual MAX chats,
  • maxgroup — MAX group chats,
  • cian — Cian chats.
chat_id string

The ID of the chat where a new outgoing or incoming message has appeared.

If the message appeared in a dialogue with a contact, this is essentially the messenger account ID of the person the user is communicating with:

  • For whatsapp and viber — phone number in international format without the + (e.g., 37190111122).
  • For instagram — username without the @ symbol.
  • For whatsgroup, maxgroup, telegroup, telegram, max, avito, vk, cian — provided in incoming message webhooks.
username string Telegram only. Telegram username without the @ symbol. Can be used when sending messages if the chat_id is unknown
phone string The contact's phone number in international format, if known. Without + or any other characters
profile_id* string

Cian only. Profile identifier.

For other chat types, this parameter will be null.

advert* object

The Cian ad that the customer messaged about.

For other chat types, this object will be null.

attachment (object)

Parameter. Required are marked with * Type Description
url* string File URL
name string File name
mimetype string MIME type of the file
size number File size in bytes
sha1 string SHA-1 hash of the file

template (object)

Parameter. Required are marked with * Type Description
id* string Template ID
values array[string] Values for template variables
buttons array[object] Template buttons
buttons[].text* string Button label
buttons[].payload* string Button payload

referral (object)

Parameter. Required are marked with * Type Description
source_url string URL to the ad or post that the customer clicked on
source_type string Specifies the type of source for the ad. Possible values: ad, post
source_id string The Meta ID for the ad or post
headline string The headline used in the ad or post
body string The body text for the ad or post
media_type string Media type in the ad or post. Possible values: image, video
image_url string The URL of the image used in the ad, applicable when media_type is image
video_url string The URL of the video used in the ad, applicable when media_type is video
thumbnail_url string The URL of the thumbnail for the video, applicable when media_type is video
ctwa_clid string The Click ID generated by Meta for ads that redirect to WhatsApp

message.status_update

{
"message_id": UUIDv4 | null,
"request_id": UUIDv4 | null,
"status": "accepted" | "sent" | "delivered" | "read" | "failed",
"reason": string | null
}

Possible reason values depending on status.

When status: "accepted" | "sent" | "delivered" | "read" — reason is always null

When status: "failed" — reason indicates the cause of the error.

Reason value Description
network_error Network error
bad_contact Contact does not exist (For WhatsApp — phone number not registered)
text_length_is_too_much Message exceeds character limit: 4096 for WhatsApp, Telegram, and MAX; 1000 for Instagram
can_not_get_content  
is_spam Messenger has blocked the message as suspected spam. If using Instagram or WhatsApp, try rephrasing and sending later. For Telegram, contact @SpamBot
to_many_requests Too many requests
bad_content The file is too large or its format is not supported by the messenger
canceled Sending was canceled
over_24_hour_limit For WABA. Daily Meta dialog session limit has been exceeded. You cannot initiate new conversations. Wait for one of the active sessions to expire, then resend
wapi_not_enough_money For WABA. Top up your WABA subscription balance to send first messages and WABA templates
target_user_is_blocked  
no_user_permissions  
waba_business_account_locked Message not sent: your WhatsApp account has been blocked by Meta. See the guide for details
message_limit_exceeded Message not sent: Meta has restricted the number of messages that can be sent from this WABA number
bad_required_parameter The request contains one or more unsupported or malformed parameters
stop_receiving_marketing_messages For WABA. The contact has opted out of receiving marketing messages. Send a template from another category
voice_messages_forbidden For Telegram. This user has disabled receiving voice messages
input_user_deactivated For Telegram. The contact has deleted their account or blocked you
privacy_premium_required For Telegram. This contact only accepts messages from accounts with Telegram Premium
allow_payment_required For Telegram. This contact only accepts messages in exchange for paid Telegram Stars
message_empty For Telegram. Add text or an attachment — Telegram does not allow empty messages
session_revoked For Telegram. Message not sent: Telegram has terminated the active Wazzup session
file_part_missing For Telegram. Failed to upload the file. Please try again
channel_private For Telegram. You are not a member of this group, or the group has been deleted
unknown_error Unknown error occurred

message.delete

{
"message_id": UUIDv4,
"crm_user_id": string | null,
"timestamp": Unix timestamp in milliseconds
}

message.edit

{
"message_id": UUIDv4,
"crm_user_id": string | null,
"timestamp": Unix timestamp in milliseconds,
"text": string,
"old_text": string,
"attachment": {},
"old_attachment": {}
}

waba_template.status_update

{
"template_id": UUIDv4,
"status": "APPROVED" | "PENDING" | "REJECTED" | "PAUSED" | "DISABLED",
"timestamp": unix epoch
}
Parameter Type Description
template_id string Template GUID
status string Template moderation status
Status value Description
APPROVED Corresponds to "Active" in the Wazzup UI. The template has been approved by Meta and is ready to use
PENDING Corresponds to "Under Moderation" in the Wazzup UI. Meta is still reviewing the template
REJECTED Corresponds to "Rejected" in the Wazzup UI. The template did not pass Meta's moderation
PAUSED Corresponds to "Rejected" in the Wazzup UI. The template received recipient complaints, Meta is re-reviewing it
DISABLED Corresponds to "Rejected" in the Wazzup UI. The template was disabled following complaints

channel.status_update

{
"channel_id": UUIDv4,
"status": "active" | "disabled" | "init",
"reason": string | null
}

Possible reason values depending on status:

  • When status: "active" — reason is always null
  • When status: "init" — reason: wait_for_password, wait_for_code, qr, sync, or null
  • When status: "disabled" — reason: blocked, not_enough_money, rejected, unauthorized, foreignphone, qridle, openelsewhere, or null
Channel status Description
active Channel is active
init Channel is starting up
disabled Channel is disabled: removed from subscription or deleted while preserving messages
reason value Description
qridle, qr QR code needs to be scanned
openelsewhere Channel is authorized in another Wazzup account
not_enough_money Channel is unpaid
foreignphone QR scanned by a different account in the messenger (different phone number)
unauthorized Unauthorized
wait_for_password Two-factor authentication password required
blocked Meta blocked the WABA channel
rejected WABA channel rejected

channel.delete

{
"channel_id": UUIDv4,
"timestamp": Unix timestamp in milliseconds
}

channel.create

{
"channel_id": UUIDv4,
"timestamp": Unix timestamp in milliseconds
}

channel.qr_update

{
"channel_id": UUIDv4,
"qr_code": string,
"platform": 'whatsapp' | 'telegram_api' | 'viber'
}

channel.waba_tier_update

Meta assigns WhatsApp Business accounts a tier — this determines how many 24‑hour conversations can be initiated per day.

{
"channel_id": UUIDv4,
"tier": "TIER_0" | "TIER_1K" | "TIER_10K" | "TIER_100K" | "TIER_UNLIMITED"
}

Possible values:

  • TIER_0: 250 conversations,
  • TIER_1K: 1000 conversations,
  • TIER_10K: 10,000 conversations,
  • TIER_100K: 100,000 conversations,
  • TIER_UNLIMITED: unlimited conversations.

group_chat.memberships_update

{
"meta": {
"idempotency_key": "945ff8a4-bb3c-45f7-8d1a-b40f7ce2fb",
"timestamp": 1760690824
},
"data": [
{
"group_id": "3c67afca-6cbf-4517-b120-c4a83dbd41",
"members": [
{
"group_member_id": "c705786d-bba5-43f9-9b-5f6f5d4ba623",
"name": "Mary",
"avatar_url": null,
"status": "in_group" | "invited" | "unknown" | "deleted" | "not_exists"
}
]
}
],
"event": "group_chat.memberships_update"
}

Webhooks for creating a contact and a deal: crm_entities.contact_add and crm_entities.deal_add

We send these when a contact or a deal needs to be created in the CRM. This happens in 3 cases:

Case 1: In integration settings, item 3 is set to "Every new client is assigned to the first respondent".

A new client writes in. An employee responds. Wazzup sends a webhook to create a new contact and deal on the first outgoing message, if pipelines with stages have been loaded from the CRM.

We only send the webhook if we have received the ID of the employee who responded to the new client's message.

Case 2: In integration settings, item 3 is set to "Sales reps are assigned new clients in turn".

A new client writes in. Wazzup sends a webhook to create a new contact and deal on the first incoming message, if pipelines with stages have been loaded from the CRM.

For webhooks to be sent in cases 1 and 2, you need to add employees in the personal account and enable the "Receives new clients" toggle for at least one of them in the integration settings.

Case 3: When the "+" button is clicked in the "Deals" list to create a deal:

We check whether the contact exists in our database.

If the contact does not exist, we send a webhook to create a contact:

{
    "event": "crm_entities.contact_add",
    "data": [
        {
            "responsible_user_id": string,
            "name": string,
            "recipient": [
                {
                    "chat_type": string,
                    "chat_id": string,
                    "username": "string",
                    "phone": "string"
                }
            ],
            "source": "auto" | "by_user"
// auto — on incoming or outgoing message, by_user — by button click
        }
    ],
    "meta": {
        "idempotency_key": string,
        "timestamp": number
    }
}

The CRM must then create the contact and return a JSON object with the contact's data. Example:

{
  "id": "string",
// contact ID in the CRM, required parameter
  "responsible_user_id": "string",
  "name": "string",
// contact name, required parameter
  "recipient": [
    {
      "chat_type": "string",
      "chat_id": "string",
      "username": "string",
      "phone": "string"
    }
  ],
  "uri": "string"
// uri is optional. Link to the contact in the CRM, which the user opens from the "Deals" list in the chats iframe
}

We will then send a webhook to create a deal.

If the contact already exists, we send a webhook to create a deal:

{
    "event": "crm_entities.deal_add",
    "data": [
        {
            "responsible_user_id": string,
            "pipeline_id": string,
            "stage_id": string,
            "contacts": string[],
            "source": "auto" | "by_user"
// auto — on incoming or outgoing message, by_user — by button click
        }
    ],
    "meta": {
        "idempotency_key": string,
        "timestamp": number
    }
}

The CRM must then create the deal and return a JSON object with the deal's data. Example:

{
    "id": "deal_123",
// deal ID in the CRM, required parameter
    "responsible_user_id": "user_456",
    "name": "Client",
    "contacts": ["contact_789", "contact_012"],
    "source": "auto",
    "closed": false,
    "uri": "https://crm.example.com/deals/deal_123"
// uri is optional. Link to the deal in the CRM, which the user opens from the "Deals" list in the chats iframe
}

If the contact or deal has already been created, Wazzup will not resend the webhook, even if the deal is closed: "closed": true

Reliability Recommendations

  • Respond quickly — return 200 OK as fast as possible; handle any additional processing asynchronously.
  • Retries — we may retry delivery in case of temporary failures (retry counts and intervals are subject to change). Make sure your processing is idempotent.
  • Security — your endpoint must be served over HTTPS.
  • Limits — avoid performing heavy work synchronously in the webhook handler. Use a queue or background processing instead.

Quick Checklist

  1. Set up a public POST endpoint (HTTPS) that accepts application/json.
  2. Store logs of responses, request bodies, and headers.
  3. Subscribe to at least message.add and message.status_update events — this gives you basic visibility into whether messages are sent and delivered.
  4. Test the following scenarios:
    • incoming message → your service → 200 OK response
    • outgoing message → status update webhooks