Skip to main content
Every delivery is a POST with a JSON body in the same envelope. The top-level fields never change; only data varies by event type.

Envelope

string
Event type. One of the values in Event types.
string
ID of the event. Use it as an idempotency key — delivery is at-least-once.
  • It holds the same value as the webhook-id header and does not change across retries of the same event.
  • If several webhooks of the instance are subscribed to the event, they all receive the same id.
  • Different events have different ids — including the DELIVERED and the READ of the same message.
  • It is an opaque value: do not extract information from it. To refer to the message, use data.messageId.
string
ID of the Zapfy instance (the connected number) that produced the event.
string
When the event happened, ISO 8601 in UTC, with second resolution. For messages and statuses it is the time recorded by WhatsApp; for connection and QR Code events, the time Zapfy registered the event. It stays the same across retries.
object
Event-specific payload, described below.

Event types

These are also the names you use in events when registering a webhook.

Conventions

  • A field without a value is omitted. It never comes as null or as an empty string: check whether it is present (if (data.quotedMessageId)). Each field below says when it is present.
  • Type-specific objects (media, location, reaction…) appear only in the message type they belong to.
  • Enum values (DIRECT, READ, BANNED…) are uppercase and part of the contract.
  • Phone numbers are digits only, with the country code and no + (5511999998888). They are the number exactly as WhatsApp has it registered, which is not always the way you typed it — see the note below.

Common objects

chat

The conversation: where the event happened and where you reply.
  • In a direct conversation, chat is the contact: in MESSAGE.RECEIVED, the contact who wrote; in MESSAGE.SENT, the contact you sent to.
  • In a group, chat is the group.
string
DIRECT (a person) · GROUP · STATUS (status updates) · BROADCAST (broadcast list) · NEWSLETTER (channel).
string
The conversation’s WhatsApp identifier. Pass it as to to reply — the API accepts it as is. Always in the same format: 5511988887777@s.whatsapp.net (a person), 81896604192873@lid (a person whose number WhatsApp hides), 120363012345678901@g.us (a group — the same id returned by GET /v1/groups), status@broadcast (status updates), …@newsletter (a channel). Use it as the key of the conversation in your system.
string
The person’s phone, digits only. Present only in DIRECT conversations, and only when WhatsApp exposes the number. Use it to filter or match by number.

sender

Who wrote the message, when someone else wrote it: in a direct conversation it is the contact themself; in a group, the participant who wrote. sender is absent when fromMe is true — in direct conversations and in groups. A message you wrote is always from your own number, so fromMe is all you need.
string
The WhatsApp identifier of who wrote, in the same format as chat.id (…@s.whatsapp.net, or …@lid when WhatsApp hides the number). Accepted in to.
string
The person’s phone. Present only when WhatsApp exposes the number.
string
The profile name the contact set on their own WhatsApp — not the name saved in your contacts. Present only when the contact has a profile name.
WhatsApp hides the phone number of some contacts. When that happens, phone is absent and id is an …@lid identifier, which still works as to. To filter by number, use phone, never id.
Brazilian mobile numbers and the 9th digit. Older Brazilian mobile lines are registered on WhatsApp without the 9th digit. You can send to 5544997075632 and receive MESSAGE.SENT with chat.phone 554497075632 (and chat.id 554497075632@s.whatsapp.net) — it is the same person.
  • To reply, always use chat.id exactly as received.
  • To compare with the numbers in your system, account for this: for a Brazilian mobile, treat 55 + area code + 8 digits and 55 + area code + 9 + 8 digits as the same number.

MESSAGE.RECEIVED / MESSAGE.SENT

Text in a direct conversation:
Image in a group, replying to another message:
string
Message ID on WhatsApp — the same value the send endpoints return. Use it to react, edit, delete, quote and to match MESSAGE.STATUS_UPDATED.
boolean
true in MESSAGE.SENT (your number wrote it), false in MESSAGE.RECEIVED.
object
The conversation. See chat.
object
Who wrote it. Present only when fromMe is false. See sender.
string
Content type. See the table below.
string
The text of the message, or the caption of the media. Present only when there is text or a caption.
string
ID of the message this one replies to. Present only when the message is a reply.
MESSAGE.SENT fires when WhatsApp accepts a message from your number. It carries the same messageId the send endpoint returned; messages typed on the phone or another linked device arrive here too. Editing or deleting a message does not generate an event.

Message types

object
Present in IMAGE, VIDEO, AUDIO, DOCUMENT and STICKER. Describes the file; the file itself is not included in the webhook. Each subfield is present only when it is known.
object
Present in LOCATION.
object
Present in CONTACT.
object
Present in REACTION.
object
Present in POLL.
object
Present in BUTTON_REPLY.
Message with buttons, as the MESSAGE.SENT of a send made through the API:
When the contact taps an option, a MESSAGE.RECEIVED with messageType BUTTON_REPLY arrives, and buttonReply.id is the ID you defined for that option.

MESSAGE.STATUS_UPDATED

What happened to a message you sent: delivered, read or not sent. The “sent” step itself is the MESSAGE.SENT event.
string[]
The messages this status refers to. It is a list because a single receipt can cover several messages — a contact opening the conversation reads all of them at once, and a single READ can carry many IDs.
string
DELIVERED (✓✓, arrived on the device) · READ (blue ✓✓) · FAILED (not sent). The order is DELIVERED < READ: ignore a status older than the one you already have. FAILED is final: that message did not reach WhatsApp and won’t.
string
Why it was not sent, as a stable code — use it to decide and to group by. Present only when status is FAILED.
  • NOT_ON_WHATSAPP — the number has no WhatsApp. Resending won’t help.
  • MEDIA_DOWNLOAD_FAILED — couldn’t download the media from the URL (down, 404). Fix the URL and resend.
  • MEDIA_INVALID — the file isn’t valid for this message type. Send another file.
  • MEDIA_UPLOAD_FAILED — WhatsApp rejected the media upload. Resend.
  • INVALID_MESSAGE — WhatsApp doesn’t accept that content there (e.g. a document to a channel, more than 3 reply buttons). Adjust the message.
  • INSTANCE_LOGGED_OUT — the instance was logged out with the message queued. Reconnect and resend.
  • INSTANCE_DELETED — the instance was deleted with the message queued.
  • WHATSAPP_ERROR — WhatsApp refused the send, even after the retries. See message.
string
What happened, in a sentence with the original error — to show or log. Present only when status is FAILED.
  • Send failures: the send answered 202 with the messageId, but the message didn’t go out. FAILED carries that same messageId, and there is no MESSAGE.SENT for it. A sent message never gets FAILED. If the instance only disconnected, queued messages wait and go out when it reconnects — they don’t become FAILED.
  • The status only carries the message IDs. The conversation and the recipient are the ones in the MESSAGE.SENT of the same message: link the two by messageIds.
  • Groups: the status means the message reached it for at least one participant. DELIVERED and READ each arrive once per message, not once per participant.
  • Read receipts: READ only arrives if read receipts are enabled on both numbers — yours and the contact’s.
  • Only the contact’s receipts. When your own number reads a message it received (opening the conversation on the phone), no status webhook is sent.

CONNECTION.UPDATED

string
Present in CONNECTED: the phone of the connected number.
string
In CONNECTED: the profile name of the connected number. Absent right after the first pairing, while WhatsApp has not sent the name yet.
string
Present in BANNED: SENT_TO_TOO_MANY_PEOPLE (too many messages to people who don’t have your number saved) · BLOCKED_BY_USERS (too many people blocked you) · CREATED_TOO_MANY_GROUPS (too many groups with people who don’t have your number) · SENT_TOO_MANY_SAME_MESSAGE (the same message to too many people) · BROADCAST_LIST (too many messages to a broadcast list) · UNKNOWN.
number
In BANNED: seconds until the ban ends. Absent when WhatsApp doesn’t say.

QRCODE.UPDATED

The pairing flow, for when you build your own pairing screen.
Pairing completes with CONNECTION.UPDATED and status: CONNECTED — that is the signal to close your pairing screen.
string
The QR Code as a data URL (base64 PNG), ready to use in an <img>.
string
The QR Code content, for when you draw the QR yourself.
number
Which QR Code this is in the current pairing attempt, starting at 1.
number
How many QR Codes are generated before EXPIRED. Absent means no limit.