> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zapfy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> The canonical envelope and each event's payload. Same shape, no matter how it connects to WhatsApp.

Every event arrives in the **same envelope**. The top-level fields never change;
only `data` varies by event type. The payload is **normalized by Zapfy** — it does
not change when the underlying connection to WhatsApp changes.

## Envelope

```json theme={null}
{
  "event": "MESSAGE_RECEIVED",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:34:56.789Z",
  "data": { }
}
```

<ResponseField name="event" type="string">
  Event type, in `UPPER_SNAKE`. One of the values below.
</ResponseField>

<ResponseField name="instanceId" type="string">
  ID of the Zapfy instance that produced the event.
</ResponseField>

<ResponseField name="id" type="string">
  Unique event ID. Use it as an **idempotency key** (delivery is at-least-once).
</ResponseField>

<ResponseField name="timestamp" type="string">
  Emission time, ISO 8601 (UTC).
</ResponseField>

<ResponseField name="data" type="object">
  Event-specific payload — described below. The examples that follow show the full
  event (envelope + `data`).
</ResponseField>

## Event types

| Event               | When it fires                            |
| ------------------- | ---------------------------------------- |
| `MESSAGE_RECEIVED`  | You received a message.                  |
| `MESSAGE_SENT`      | You sent a message.                      |
| `MESSAGE_STATUS`    | A message was delivered, read or failed. |
| `CONNECTION_UPDATE` | Your number connected or disconnected.   |
| `QRCODE_UPDATED`    | A new QR Code was generated to connect.  |

## MESSAGE\_RECEIVED / MESSAGE\_SENT

Message in a **contact chat** (1:1):

```json theme={null}
{
  "event": "MESSAGE_RECEIVED",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:34:56.789Z",
  "data": {
    "messageId": "3EB0C767D...",
    "fromMe": false,
    "chat": {
      "jid": "5511999998888@s.whatsapp.net",
      "phone": "5511999998888",
      "isGroup": false,
      "name": null
    },
    "sender": {
      "jid": "5511999998888@s.whatsapp.net",
      "phone": "5511999998888",
      "name": "John Doe"
    },
    "type": "TEXT",
    "text": "Hi, how are you?",
    "media": null
  }
}
```

Message in a **group** — `chat` is the group (`phone: null`) and `sender` is the participant who sent it:

```json theme={null}
{
  "event": "MESSAGE_RECEIVED",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:34:56.789Z",
  "data": {
    "messageId": "3EB0C767D...",
    "fromMe": false,
    "chat": {
      "jid": "120363012345678901@g.us",
      "phone": null,
      "isGroup": true,
      "name": "Family"
    },
    "sender": {
      "jid": "5511999998888@s.whatsapp.net",
      "phone": "5511999998888",
      "name": "John Doe"
    },
    "type": "TEXT",
    "text": "Hey group!",
    "media": null
  }
}
```

The fields below describe the `data` object:

<ResponseField name="messageId" type="string">
  Message ID on WhatsApp. Reuse it in `delete`/`edit`.
</ResponseField>

<ResponseField name="fromMe" type="boolean">
  `true` if you sent it (`MESSAGE_SENT`); `false` if you received it.
</ResponseField>

<ResponseField name="chat" type="object">
  The conversation — the contact or group the message belongs to. This is where you reply.

  <Expandable title="chat">
    <ResponseField name="jid" type="string">
      Conversation JID (contact `…@s.whatsapp.net` or group `…@g.us`). Use it as the recipient to reply.
    </ResponseField>

    <ResponseField name="phone" type="string | null">
      Phone in E.164 (digits only) when the conversation is a contact; `null` for groups.
    </ResponseField>

    <ResponseField name="isGroup" type="boolean">
      `true` when the conversation is a group.
    </ResponseField>

    <ResponseField name="name" type="string | null">
      Group name; `null` for contact conversations.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="sender" type="object">
  Who sent this message. In a contact chat it's the contact itself; in a group, the participant.

  <Expandable title="sender">
    <ResponseField name="jid" type="string">
      JID of who sent it. Stable identifier even when the number isn't available.
    </ResponseField>

    <ResponseField name="phone" type="string | null">
      Sender's phone in E.164; `null` when WhatsApp anonymizes the sender (`@lid`).
    </ResponseField>

    <ResponseField name="name" type="string | null">
      Sender's display name (pushName).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="type" type="string">
  `TEXT` · `IMAGE` · `AUDIO` · `VIDEO` · `DOCUMENT` · `STICKER` · `LOCATION` ·
  `CONTACT` · `INTERACTIVE`.
</ResponseField>

<ResponseField name="text" type="string">
  Text content. For media, holds the caption.
</ResponseField>

<ResponseField name="media" type="object | null">
  Present for media: `{ mimeType, url, filename, caption }`.
</ResponseField>

## MESSAGE\_STATUS

Status of a message **you sent** — `data` carries `chat` (same shape), no `sender`:

```json theme={null}
{
  "event": "MESSAGE_STATUS",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:35:01Z",
  "data": {
    "messageId": "3EB0C767D...",
    "chat": {
      "jid": "5511999998888@s.whatsapp.net",
      "phone": "5511999998888",
      "isGroup": false,
      "name": null
    },
    "status": "DELIVERED",
    "error": null
  }
}
```

<ResponseField name="status" type="string">
  `PENDING` · `SENT` · `DELIVERED` · `READ` · `FAILED`.
</ResponseField>

<ResponseField name="error" type="object | null">
  Present on `FAILED`: `{ code, message }`.
</ResponseField>

## CONNECTION\_UPDATE

```json theme={null}
{
  "event": "CONNECTION_UPDATE",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:34:56.789Z",
  "data": {
    "status": "CONNECTED",
    "phone": "5511777776666",
    "reason": null
  }
}
```

<ResponseField name="status" type="string">
  `CONNECTING` · `CONNECTED` · `DISCONNECTED`.
</ResponseField>

<ResponseField name="phone" type="string | null">
  Connected account's phone number, in E.164. `null` while there is no session (e.g. `CONNECTING` or `DISCONNECTED`).
</ResponseField>

<ResponseField name="reason" type="string | null">
  Disconnect reason when `DISCONNECTED` (e.g. `LOGGED_OUT`, `BANNED`).
</ResponseField>

## QRCODE\_UPDATED

```json theme={null}
{
  "event": "QRCODE_UPDATED",
  "instanceId": "01J9Z...",
  "id": "evt_01J9Z...",
  "timestamp": "2026-06-09T12:35:55Z",
  "data": {
    "qr": "data:image/png;base64,iVBOR...",
    "expiresAt": "2026-06-09T12:36:55Z"
  }
}
```

<ResponseField name="qr" type="string">
  QR Code as a data URL (base64 PNG) to pair the number.
</ResponseField>

<ResponseField name="expiresAt" type="string">
  QR expiration, ISO 8601 (UTC).
</ResponseField>
