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

# Webhooks

> Get real-time message and device events at your HTTPS endpoint

# Webhooks

Webhooks push signed HTTP POST requests to your server when something important happens—new messages, delivery updates, or device status changes—so you do not have to poll.

## Set up a webhook

<Steps>
  <Step title="Add your URL">
    In the dashboard open **Developers → Webhooks** and add an HTTPS endpoint, or create one with the API (below).
  </Step>

  <Step title="Pick events">
    Subscribe only to what you handle, for example `message.received` and `message.delivered`.
  </Step>

  <Step title="Verify signatures">
    Check `X-Wazzapi-Signature` on every request before you trust the body.
  </Step>
</Steps>

## Manage webhooks with the API

| Action           | Method   | Endpoint                                     |
| :--------------- | :------- | :------------------------------------------- |
| List             | `GET`    | `/api/v1/webhooks/subscriptions`             |
| Create           | `POST`   | `/api/v1/webhooks/subscriptions`             |
| Get              | `GET`    | `/api/v1/webhooks/subscriptions/{id}`        |
| Update           | `PATCH`  | `/api/v1/webhooks/subscriptions/{id}`        |
| Delete           | `DELETE` | `/api/v1/webhooks/subscriptions/{id}`        |
| Delivery history | `GET`    | `/api/v1/webhooks/subscriptions/{id}/events` |
| Test endpoint    | `POST`   | `/api/v1/webhooks/subscriptions/{id}/ping`   |

Official SDKs: `client.webhook_subscriptions` (Python) and `client.webhookSubscriptions` (Node).

## Security

Payloads are signed with **HMAC-SHA256**. Always verify against the **raw body bytes**, not a re-serialized JSON string.

Delivery headers:

* `X-Wazzapi-Signature` — `sha256=<hex digest>` of the body using your webhook secret
* `X-Wazzapi-Event` — event name
* `X-Wazzapi-Event-ID` — unique delivery id (use for idempotency)

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

function verifySignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}
```

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

def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, f"sha256={digest}")
```

<Tip>
  Use a constant-time comparison (as above) so timing attacks are harder.
</Tip>

## Events you can subscribe to

| Event                 | When it fires                           |
| :-------------------- | :-------------------------------------- |
| `message.received`    | Someone messaged your linked number     |
| `message.sent`        | WhatsApp accepted your outbound message |
| `message.delivered`   | The recipient’s device got the message  |
| `message.read`        | The recipient opened the message        |
| `message.failed`      | Outbound send failed                    |
| `device.connected`    | A device is healthy again               |
| `device.disconnected` | A device dropped or needs attention     |

## Example payload

```json theme={null}
{
  "id": "0b97762a-b93e-4b32-9441-3c99ba4cc0ff",
  "event_type": "message.delivered",
  "timestamp": "2026-04-26T10:20:22Z",
  "organization_id": "0f1d6e38-d6bc-49be-9c39-1fcf2f946d7e",
  "webhook_id": "a17d6351-d8f6-4cd8-ae0e-fce090afdb8f",
  "data": {
    "message_id": "8806d2db-61c7-4dd3-804e-4e90b65876d2",
    "status": "delivered",
    "phone_number": "6281234567890",
    "whatsapp_account_id": "c053d8ef-6c19-4ecb-9cc5-a4a64be79d92"
  }
}
```

See the [API Reference → Webhooks](/api-reference) for full event schemas and examples.

## Retries

If your endpoint does not return **2xx** within about 30 seconds, WazzAPI retries with backoff:

1 min → 5 min → 15 min → 30 min → 1 h → 2 h → 4 h → 8 h → 24 h

Return quickly (accept and queue work) so deliveries succeed on the first try.
