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

# Events Reference

> Real-time WhatsApp events delivered via Webhooks or SSE

WSAPI emits real-time events whenever something happens on your WhatsApp instance — new messages, status changes, group updates, calls, and more.

## Delivery modes

There are two ways to receive events:

<Tabs>
  <Tab title="Webhooks (Push)">
    WSAPI sends an HTTP `POST` request to a public endpoint you configure. Best for server-based applications that already expose HTTP endpoints.

    1. Configure your instance with **Webhook** delivery mode and specify a **Webhook URL**
    2. When an event occurs, WSAPI sends a `POST` request with the event payload
    3. Your server processes the event and responds with a `200` status code
  </Tab>

  <Tab title="SSE (Pull)">
    Your application opens a persistent **Server-Sent Events** connection to WSAPI and receives events as they occur. Best when you don't have a public endpoint or prefer a pull-based approach.

    1. Your app opens an SSE connection to WSAPI
    2. Events are streamed in real time as they occur
    3. If the connection drops, your app reconnects and resumes

    <Info>
      We recommend using SSE mode with one of our [official SDKs](/sdks/overview), which include a proper SSE client implementation with automatic reconnection.
    </Info>
  </Tab>
</Tabs>

See [Configuring Event Delivery](/basics/configuring-instance) for setup instructions.

## Event payload

All events follow this structure regardless of delivery mode:

```json theme={null}
{
  "eventId": "evt_a1b2c3d4e5f67890",
  "instanceId": "ins_ABCdefGHIjk",
  "receivedAt": "2025-05-19T00:51:44.816Z",
  "eventType": "message",
  "eventData": {
    // Event-specific data
  }
}
```

| Field        | Type     | Description                                    |
| ------------ | -------- | ---------------------------------------------- |
| `eventId`    | `string` | Unique event identifier                        |
| `instanceId` | `string` | The instance that generated the event          |
| `receivedAt` | `string` | ISO 8601 timestamp when the event was received |
| `eventType`  | `string` | The event type (e.g., `message`, `logged_in`)  |
| `eventData`  | `object` | Event-specific payload                         |

## Event types

| Event                   | Category    | Description                                                 |
| ----------------------- | ----------- | ----------------------------------------------------------- |
| `logged_in`             | Session     | Instance connected to WhatsApp                              |
| `logged_out`            | Session     | Instance disconnected (includes reason)                     |
| `login_error`           | Session     | Pairing error occurred                                      |
| `initial_sync_finished` | Session     | Initial history sync completed                              |
| `message`               | Messages    | New message received (text, media, reaction, contact, etc.) |
| `message_read`          | Messages    | Message read/delivery receipt                               |
| `message_delete`        | Messages    | Message was deleted                                         |
| `message_star`          | Messages    | Message was starred/unstarred                               |
| `message_history_sync`  | Messages    | On-demand history sync messages                             |
| `chat_setting`          | Chats       | Chat settings changed (mute, pin, read, archive, ephemeral) |
| `chat_presence`         | Chats       | Contact is typing, recording, or paused                     |
| `chat_push_name`        | Chats       | Contact's push name changed                                 |
| `chat_status`           | Chats       | Chat about/status text updated                              |
| `chat_picture`          | Chats       | Chat picture changed                                        |
| `contact`               | Contacts    | Contact information updated                                 |
| `group`                 | Groups      | Group/community settings or membership changed              |
| `newsletter`            | Newsletters | Newsletter/channel subscription or mute change              |
| `user_presence`         | Users       | User online/offline status changed                          |
| `call_offer`            | Calls       | Incoming call received                                      |
| `call_accept`           | Calls       | Call was accepted                                           |
| `call_terminate`        | Calls       | Call ended                                                  |

<Note>
  Four events are **system events**: `logged_in`, `logged_out`, `login_error`, and `initial_sync_finished`. System events are always delivered regardless of [event filter](/basics/event-filtering) configuration and cannot be opted out of.
</Note>

## Webhook delivery headers

Every webhook `POST` request includes the following HTTP headers:

| Header                | Description                                                                                   |
| --------------------- | --------------------------------------------------------------------------------------------- |
| `Content-Type`        | Always `application/json`                                                                     |
| `X-Instance-Id`       | The ID of the instance that generated the event                                               |
| `X-Webhook-Signature` | HMAC-SHA256 signature of the request body. Only present when a `signingSecret` is configured. |

## Verifying event signatures

When a **Signing Secret** is configured for your instance, WSAPI signs every event payload using HMAC-SHA256. The signature is included in the `X-Webhook-Signature` header with the format:

```
sha256=<hex-encoded HMAC-SHA256>
```

<Warning>
  Always configure a signing secret to verify the authenticity of incoming events and prevent unauthorized parties from sending fake events to your endpoint.
</Warning>

To verify an event:

1. Compute the HMAC-SHA256 of the raw request body using your signing secret as the key
2. Hex-encode the result and prepend `sha256=`
3. Compare the computed value with the `X-Webhook-Signature` header using a **constant-time comparison** to prevent timing attacks

<CodeGroup>
  ```javascript Node.js theme={null}
  const crypto = require("crypto");

  function verifySignature(body, secret, headerValue) {
    const expected =
      "sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex");
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(headerValue)
    );
  }
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(body: bytes, secret: str, header_value: str) -> bool:
      expected = "sha256=" + hmac.new(
          secret.encode(), body, hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(expected, header_value)
  ```

  ```csharp C# theme={null}
  using System.Security.Cryptography;
  using System.Text;

  bool VerifySignature(byte[] body, string secret, string headerValue)
  {
      using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
      var hash = hmac.ComputeHash(body);
      var expected = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();
      return CryptographicOperations.FixedTimeEquals(
          Encoding.UTF8.GetBytes(expected),
          Encoding.UTF8.GetBytes(headerValue));
  }
  ```
</CodeGroup>

## Delivery and retries

* WSAPI expects a `2xx` response for webhook deliveries
* If no valid response is received, WSAPI retries delivery **up to 2 times**
* After that, the event is discarded
* Implement **idempotent** processing to handle potential duplicate deliveries

## Best practices

* **Respond quickly** — return `200` immediately and process events asynchronously
* **Verify signatures** — always validate the `X-Webhook-Signature` on incoming events
* **Handle duplicates** — design your handler to be idempotent in case of retries
* **Filter events** — use [event filtering](/basics/event-filtering) to only receive events you need
* **Use HTTPS** — always use an HTTPS endpoint for your webhook URL
* **Log events** — log incoming events for debugging and audit purposes
