> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.muchau.com.br/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.muchau.com.br/_mcp/server.

# Webhooks

> Configure webhooks para receber eventos do WhatsApp em tempo real na sua aplicação.

## Visão geral

Webhooks permitem que o Muchaw DEV envie notificações de eventos em tempo real para o seu servidor. Quando uma mensagem é recebida, um status de entrega muda ou um número é ativado, o Muchaw DEV envia uma requisição HTTP `POST` para as suas URLs de webhook configuradas.

> **Note**
>
> Esta página cobre **seus webhooks** — as URLs que o Muchaw DEV chama no seu servidor. Isso é separado do webhook interno da Meta que o Muchaw DEV usa para receber eventos da Meta.

## Eventos disponíveis

| Evento                | Disparado quando                                              |
| --------------------- | ------------------------------------------------------------- |
| `message.received`    | Uma mensagem WhatsApp recebida chega                          |
| `message.sent`        | Uma mensagem é aceita pelos servidores da Meta                |
| `message.delivered`   | Uma mensagem é entregue ao dispositivo do destinatário        |
| `message.read`        | Uma mensagem é lida pelo destinatário                         |
| `message.failed`      | Uma entrega de mensagem falha                                 |
| `number.activated`    | Um número de WhatsApp conclui o provisionamento e está pronto |
| `number.disconnected` | Um número é desconectado                                      |
| `number.banned`       | Um número é banido pela Meta                                  |

## Criando um webhook

```bash
curl -X POST https://dev.muchau.com.br/api/webhooks \
  -H "Authorization: Bearer $MUCHAW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://seu-servidor.com/webhooks/muchaw",
    "events": ["message.received", "number.activated"]
  }'
```

**Resposta** (salve o `secret` — ele é exibido apenas uma vez):

```json
{
  "id": "wh_01j...",
  "url": "https://seu-servidor.com/webhooks/muchaw",
  "secret": "whsec_...",
  "events": ["message.received", "number.activated"],
  "numberIds": [],
  "active": true,
  "createdAt": "2024-01-15T10:00:00.000Z"
}
```

### Filtrar por número

Para receber eventos apenas de números específicos, passe `numberIds`:

```json
{
  "url": "https://seu-servidor.com/webhooks/muchaw",
  "events": ["message.received"],
  "numberIds": ["clxyz123..."]
}
```

## Verificando assinaturas de webhook

Cada requisição de webhook do Muchaw DEV inclui um header `X-Muchaw-Signature` com uma assinatura HMAC-SHA256.

**Sempre verifique esta assinatura** antes de processar o evento.

**`Node.js`**

```javascript Node.js
import crypto from "node: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}`)
  );
}

// Exemplo com Express
app.post("/webhooks/muchaw", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-muchaw-signature"];
  if (!verifySignature(req.body, sig, process.env.MUCHAW_WEBHOOK_SECRET)) {
    return res.status(401).send("Assinatura inválida");
  }
  const event = JSON.parse(req.body.toString());
  // processar evento...
  res.sendStatus(200);
});
```

**`Python`**

```python Python
import hmac
import hashlib
import os

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

# Exemplo com FastAPI
@app.post("/webhooks/muchaw")
async def handle_webhook(request: Request):
    raw_body = await request.body()
    sig = request.headers.get("x-muchaw-signature", "")
    if not verify_signature(raw_body, sig, os.environ["MUCHAW_WEBHOOK_SECRET"]):
        raise HTTPException(status_code=401)
    event = json.loads(raw_body)
    # processar evento...
    return {"ok": True}
```

## Estrutura do payload de webhook

```json
{
  "event": "message.received",
  "timestamp": "2024-01-15T10:05:00.000Z",
  "data": {
    "id": "msg_01j...",
    "numberId": "clxyz123...",
    "direction": "INBOUND",
    "fromNumber": "5511888880000",
    "toNumber": "5511999990000",
    "type": "text",
    "body": { "body": "Olá!" },
    "createdAt": "2024-01-15T10:05:00.000Z"
  }
}
```

## Política de retry

Se o seu servidor retornar uma resposta não-2xx ou atingir o timeout, o Muchaw DEV tenta novamente com backoff exponencial:

| Tentativa | Intervalo     |
| --------- | ------------- |
| 1ª retry  | \~30 segundos |
| 2ª retry  | \~5 minutos   |
| 3ª retry  | \~30 minutos  |

Após 3 tentativas com falha, o evento é descartado. Se um webhook acumular **10 falhas consecutivas**, ele é desativado automaticamente.

## Rotacionando o secret

```bash
curl -X POST https://dev.muchau.com.br/api/webhooks/<id>/rotate-secret \
  -H "Authorization: Bearer $MUCHAW_API_KEY"
```

Atualize o secret na configuração do seu servidor antes que o antigo expire.

## Schema completo de Webhook

```typescript
type Webhook = {
  id: string;
  url: string;
  secret?: string;            // Presente apenas nas respostas de criação ou rotate-secret
  events: WebhookEvent[];
  numberIds: string[];        // Array vazio = todos os números
  active: boolean;
  failureCount?: number;      // Contagem de falhas consecutivas
  lastStatusCode: number | null;
  lastTriggeredAt: string | null;
  createdAt: string;
  updatedAt?: string;
};

type WebhookEvent =
  | "message.received"
  | "message.sent"
  | "message.delivered"
  | "message.read"
  | "message.failed"
  | "number.activated"
  | "number.disconnected"
  | "number.banned";
```

## Testando webhooks

`POST /api/webhooks/:id/test` envia um payload de teste para a URL configurada:

```json
{
  "success": true,
  "statusCode": 200,
  "responseTimeMs": 145,
  "message": "Test webhook delivered successfully"
}
```