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

# Visão Geral

> Eventos enviados pelo Coexy — o payload segue o formato nativo da Meta (WhatsApp Cloud API).

O Coexy entrega eventos via `POST` com `Content-Type: application/json`. O header `x-coexy-signature` carrega a assinatura HMAC-SHA256 — [verifique-a](/receiving-events/security) antes de processar qualquer dado.

## Headers de cada entrega

```
X-Coexy-Signature: sha256=<hex>   — assinatura HMAC do body
User-Agent:        Coexy-Webhooks/1.0
Content-Type:      application/json
```

## Formato do payload

O body entregue é o **payload nativo da Meta** (WhatsApp Cloud API), sem transformações. O Coexy funciona como relay transparente — a estrutura dos eventos é idêntica à que a Meta enviaria diretamente.

```json theme={null}
{
  "object": "whatsapp_business_account",
  "entry": [{
    "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
    "changes": [{
      "value": {
        "messaging_product": "whatsapp",
        "metadata": {
          "display_phone_number": "15550783881",
          "phone_number_id": "<PHONE_NUMBER_ID>"
        },
        "contacts": [{ "profile": { "name": "João Silva" }, "wa_id": "5511987654321" }],
        "messages": [ /* mensagens recebidas */ ],
        "statuses": [ /* atualizações de status */ ]
      },
      "field": "messages"
    }]
  }]
}
```

Para a estrutura completa de cada tipo de evento, consulte a documentação oficial da Meta:

* [Exemplos de payload (Meta)](https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/payload-examples)
* [Referência de campos (Meta)](https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components)

## Processando eventos

```typescript theme={null}
async function handleWebhook(req: Request): Promise<Response> {
  const body = await req.text()

  // 1. Verificar assinatura HMAC — obrigatório (ver Segurança)
  const valid = await verifySignature(body, req.headers.get('x-coexy-signature') ?? '', secret)
  if (!valid) return new Response('Unauthorized', { status: 401 })

  // 2. Responder 200 IMEDIATAMENTE — o Coexy considera falha após 10s
  processPayload(JSON.parse(body)).catch(console.error)
  return new Response('OK', { status: 200 })
}

function processPayload(payload: unknown): void {
  const data = payload as { entry?: Array<{ changes?: Array<{ value: Record<string, unknown> }> }> }

  for (const entry of data.entry ?? []) {
    for (const change of entry.changes ?? []) {
      const value = change.value

      // Mensagens recebidas
      for (const msg of (value.messages as unknown[]) ?? []) {
        handleMessage(msg, value)
      }

      // Status de mensagens enviadas
      for (const status of (value.statuses as unknown[]) ?? []) {
        handleStatus(status)
      }
    }
  }
}
```

<Tip>
  Use o campo `id` de cada mensagem (o `wamid`) como chave de idempotência ao persistir. O mesmo evento pode chegar mais de uma vez em retentativas automáticas — um `upsert` com `on conflict (wamid) do nothing` garante que não haverá duplicatas.
</Tip>
