> 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.

# Recebendo Mensagens via Webhook

> Configure um servidor de webhook para receber mensagens do WhatsApp e atualizações de status em tempo real.

## Visão geral

Quando um usuário do WhatsApp envia uma mensagem para um dos seus números conectados, o Muchaw DEV roteia essa mensagem para a sua URL de webhook. Este guia mostra como configurar um servidor de webhook que recebe e processa esses eventos com segurança.

## Passo 1 — Crie 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/whatsapp",
    "events": [
      "message.received",
      "message.delivered",
      "message.read",
      "message.failed"
    ]
  }'
```

Salve o `secret` da resposta — você precisará dele para verificar as requisições recebidas.

```bash
export MUCHAW_WEBHOOK_SECRET=whsec_...
```

## Passo 2 — Construa o handler do webhook

**`Node.js (Express)`**

```javascript Node.js (Express)
import express from "express";
import crypto from "node:crypto";

const app = express();

function verifySignature(rawBody, signature, secret) {
  if (!signature?.startsWith("sha256=")) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature.slice(7)),
    Buffer.from(expected)
  );
}

// Use raw body parser para preservar os bytes originais na verificação de assinatura
app.post(
  "/webhooks/whatsapp",
  express.raw({ type: "application/json" }),
  (req, res) => {
    // 1. Verificar assinatura
    const signature = req.headers["x-muchaw-signature"];
    if (!verifySignature(req.body, signature, process.env.MUCHAW_WEBHOOK_SECRET)) {
      return res.status(401).json({ error: "Assinatura inválida" });
    }

    // 2. Responder imediatamente (antes de processar)
    res.sendStatus(200);

    // 3. Parsear e processar
    const event = JSON.parse(req.body.toString());
    handleEvent(event);
  }
);

function handleEvent(event) {
  switch (event.event) {
    case "message.received":
      console.log("Nova mensagem de", event.data.fromNumber, ":", event.data.body);
      break;
    case "message.delivered":
      console.log("Mensagem", event.data.id, "entregue");
      break;
    case "message.read":
      console.log("Mensagem", event.data.id, "lida");
      break;
    case "message.failed":
      console.error("Falha na entrega da mensagem", event.data.id);
      break;
  }
}

app.listen(3001, () => console.log("Servidor de webhook na porta 3001"));
```

**`Python (FastAPI)`**

```python Python (FastAPI)
import hmac
import hashlib
import json
import os
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    if not signature.startswith("sha256="):
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature[7:], expected)

@app.post("/webhooks/whatsapp")
async def webhook_handler(request: Request):
    raw_body = await request.body()
    signature = request.headers.get("x-muchaw-signature", "")

    if not verify_signature(raw_body, signature, os.environ["MUCHAW_WEBHOOK_SECRET"]):
        raise HTTPException(status_code=401, detail="Assinatura inválida")

    event = json.loads(raw_body)
    handle_event(event)  # Processar async/em background se necessário
    return {"ok": True}

def handle_event(event: dict):
    match event["event"]:
        case "message.received":
            msg = event["data"]
            print(f"Nova mensagem de {msg['fromNumber']}: {msg['body']}")
        case "message.delivered":
            print(f"Mensagem {event['data']['id']} entregue")
        case "message.read":
            print(f"Mensagem {event['data']['id']} lida")
```

> **Warning**
>
> **Sempre responda com 200 antes de processar.** O Muchaw DEV aguarda até 10 segundos por uma resposta. Se seu processamento demorar mais, responda imediatamente e processe o evento de forma assíncrona.

## Passo 3 — Exponha seu servidor publicamente

Para desenvolvimento local, use um tunnel como o [ngrok](https://ngrok.com):

```bash
ngrok http 3001
# → Forwarding: https://abc123.ngrok.io → localhost:3001
```

Atualize a URL do seu webhook para a URL do ngrok.

## Passo 4 — Teste o webhook

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

Resposta:

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

## Estrutura do evento de webhook

Todos os eventos seguem o mesmo envelope:

```json
{
  "event": "message.received",
  "timestamp": "2024-01-15T10:05:00.000Z",
  "data": { ... }
}
```

O formato de `data` depende do tipo de evento. Para eventos `message.*`, é um objeto Message. Para eventos `number.*`, é um objeto Number.

## Lidando com retries

O Muchaw DEV tenta novamente os eventos até 3 vezes com backoff exponencial. Seu handler deve ser **idempotente** — processar o mesmo evento duas vezes não deve causar efeitos colaterais (registros duplicados, envios duplicados, etc.).

Use `event.data.id` como chave de deduplicação:

```javascript
const processed = new Set();
function handleEvent(event) {
  const key = `${event.event}:${event.data.id}`;
  if (processed.has(key)) return;
  processed.add(key);
  // processar...
}
```

Em produção, use Redis ou seu banco de dados para deduplicação.