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

# Webhooks

> Manage webhook endpoints and verify signed events sent by MsgFlash.

## À quoi servent les webhooks ?

MsgFlash can send signed HTTP requests to your application for each important event:

* `instance.connected`
* `instance.disconnected`
* `message.sent`
* `message.delivered`
* `message.read`
* `message.failed`
* `message.received`

***

## Manage your endpoints through the public API

### Parameters

#### `POST /api/v1/webhooks`

| Field    | Type      | Required | Location | Description            |
| -------- | --------- | -------- | -------- | ---------------------- |
| `url`    | string    | yes      | body     | URL HTTPS de réception |
| `events` | string\[] | yes      | body     | 1 à 50 événements      |

#### `DELETE /api/v1/webhooks/{id}`

| Parameter | Type | Required | Location | Description   |
| --------- | ---- | -------- | -------- | ------------- |
| `id`      | UUID | yes      | path     | ID du webhook |

### Listr

```bash theme={null}
curl https://srv.msgflash.com/api/v1/webhooks \
  -H "x-api-key: msgf_live_your_api_key_here"
```

### Create

```bash theme={null}
curl -X POST https://srv.msgflash.com/api/v1/webhooks \
  -H "x-api-key: msgf_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/msgflash",
    "events": ["message.delivered", "message.failed", "message.read"]
  }'
```

Response:

```json theme={null}
{
  "data": {
    "id": "wh_uuid",
    "url": "https://example.com/webhooks/msgflash",
    "events": ["message.delivered", "message.failed", "message.read"],
    "secret": "generated_once_secret",
    "createdAt": "2026-04-01T10:00:00.000Z"
  }
}
```

<Warning>
  Save the secret immediately. It is required to verify the HMAC signature.
</Warning>

### Delete

```bash theme={null}
curl -X DELETE https://srv.msgflash.com/api/v1/webhooks/WEBHOOK_ID \
  -H "x-api-key: msgf_live_your_api_key_here"
```

### Common errors

| Code                             | HTTP | When                  |
| -------------------------------- | ---- | --------------------- |
| `WEBHOOKS_NOT_AVAILABLE_ON_PLAN` | 403  | Feature no disponible |
| `MAX_WEBHOOK_ENDPOINTS_REACHED`  | 403  | Limit reached         |
| `NOT_FOUND`                      | 404  | Webhook not found     |

***

## Headers sent by MsgFlash

| Header                           | Description              |
| -------------------------------- | ------------------------ |
| `Content-Type: application/json` | Payload JSON             |
| `X-MsgFlash-Signature`           | Signature `sha256=<hex>` |
| `X-MsgFlash-Event`               | Name of l'événement      |

***

## Example de payload

```json theme={null}
{
  "event": "message.delivered",
  "instanceId": "inst_uuid",
  "messageId": "msg_uuid",
  "providerMessageId": "BAE5D1A2B3C4D5E6",
  "to": "+33612345678",
  "status": "delivered",
  "timestamp": "2026-04-01T10:00:05.000Z"
}
```

***

## Verify the signature

Algorithme : `HMAC-SHA256(secret, rawBody)`

<Tabs>
  <Tab title="Node.js">
    ```ts theme={null}
    import crypto from 'crypto'
    import express from 'express'

    const app = express()

    app.post('/webhooks/msgflash', express.raw({ type: 'application/json' }), (req, res) => {
      const signature = req.headers['x-msgflash-signature'] as string
      const secret = process.env.MSGFLASH_WEBHOOK_SECRET!

      const expected = 'sha256=' + crypto
        .createHmac('sha256', secret)
        .update(req.body)
        .digest('hex')

      if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
        return res.status(401).send('Invalid signature')
      }

      res.sendStatus(200)
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac
    import hashlib
    import os

    WEBHOOK_SECRET = os.environ["MSGFLASH_WEBHOOK_SECRET"]

    def verify(signature: str, raw_body: bytes) -> bool:
        expected = "sha256=" + hmac.new(
            WEBHOOK_SECRET.encode("utf-8"),
            raw_body,
            hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(signature, expected)
    ```
  </Tab>
</Tabs>

***

## Livraison et retry

* 5 tentatives
* backoff exponentiel
* any no-2xx response triggers a retry

Répondez `200` rapidement puis traitez en asynchrone.
