O exemplo oficial é um projeto Node.js executável no repositório público botozap-js/examples/agent-endpoint. Ele usa apenas o SDK e a API públicos: a BotoZap continua sendo o canal oficial e o modelo/runtime do agente continua sendo seu.
Fluxo orientado a Eventos
Meta → BotoZap (inbox/outbox) → seu Endpoint
│
INSERT + NOTIFY
│
ACK 2xx rápido
▼
worker do agente
│
POST /v1/messages → MetaO Endpoint valida o HMAC sobre os bytes crus, grava o Evento numa fila PostgreSQL e só então confirma com 2xx. O ACK não espera o agente nem a Meta responder. Depois do commit, LISTEN/NOTIFY acorda o worker; a linha durável continua sendo a fonte da verdade caso um processo reinicie.
A coluna idempotency_key é única. Retry ou replay com a mesma X-Idempotency-Key encontra o job existente e não produz outra mensagem outbound. Veja também Confiabilidade e entrega.
Rodar o exemplo
git clone https://github.com/bytecraft-fernando/botozap-js.git
cd botozap-js
pnpm install
export DATABASE_URL='postgresql://app:senha@localhost:5432/app'
export BOTOZAP_API_KEY='bz_live_xxx'
export BOTOZAP_WEBHOOK_SECRET='whsec_xxx'
export BOTOZAP_FALLBACK_TEMPLATE='retomar_atendimento'
pnpm --filter example-agent-endpoint startPara produção, rode start:endpoint e start:worker como processos separados. Ambos usam o mesmo PostgreSQL; não existe fila em memória entre eles. Publique a rota https://seu-dominio.example/webhooks/botozap. Um túnel é útil no desenvolvimento, mas não deve virar Endpoint permanente.
Sem configuração de modelo, a resposta local é determinística. Com AGENT_ENDPOINT_URL, o worker envia { input, event } ao seu runtime e espera { reply }. O README do exemplo documenta o contrato.
Criar o Endpoint
Cadastre a URL em Webhooks no painel ou pela API. A resposta 201 é a única leitura que contém o secret; guarde-o em BOTOZAP_WEBHOOK_SECRET.
curl -sS https://botozap.com.br/api/v1/webhooks \
-H "Authorization: Bearer $BOTOZAP_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://seu-dominio.example/webhooks/botozap",
"events": ["messages"],
"active": true
}'Testar sem enviar WhatsApp
curl -sS -X POST \
https://botozap.com.br/api/v1/webhooks/$ENDPOINT_ID/test \
-H "Authorization: Bearer $BOTOZAP_API_KEY"O teste usa os mesmos headers, assinatura e caminho HTTP de uma Entrega real. O exemplo persiste webhook.test, confirma o ACK e o marca como ignored; só whatsapp.message.received com texto aciona uma resposta.
Janela de 24h e Template
O worker usa o timestamp inbound com margem de segurança: envia texto livre dentro da janela e o Template aprovado configurado em BOTOZAP_FALLBACK_TEMPLATE fora dela. A API BotoZap ainda é a autoridade. Se a janela fechar entre a decisão e o envio, ela responde outside_window antes de chamar a Meta e o adapter troca para Template sem gerar duas mensagens.
Antes do I/O outbound, o job muda para sending. Esse estado nunca é retomado automaticamente: uma queda nesse ponto pode exigir inspeção humana, mas não dispara uma resposta duplicada para o Contato.
Observar Entregas
curl -sS \
"https://botozap.com.br/api/v1/webhook_deliveries?webhook_id=$ENDPOINT_ID" \
-H "Authorization: Bearer $BOTOZAP_API_KEY"A listagem mostra status, response_code, número de tentativas e próxima retentativa. No banco do exemplo, consulte agent_jobs sem imprimir payload para observar os estados sem expor a conversa.
Replay
Depois de corrigir um Endpoint cuja Entrega chegou a exhausted, um owner/admin pode usar Operação → Replay. A plataforma reenvia o mesmo corpo com a mesma chave. O exemplo responde 200 duplicate e não cria outro job. A Entrega permanece observável e o replay é auditado.
Se a Entrega teve sucesso, mas o runtime do agente esgotou três tentativas, o estado failed é a DLQ local. Rode pnpm --filter example-agent-endpoint replay -- <job-id>para fazer failed → queued e acordar o worker. O comando recusa qualquer job que não esteja em failed.
Não retente automaticamente jobs locais em sending ou ambiguous: a Meta não fornece uma chave de idempotência no envio e uma tentativa anterior pode ter sido aceita.
Endpoint BotoZap não é receiver Meta
| Endpoint do agente | Receiver Meta da BotoZap | |
|---|---|---|
| Quem chama | BotoZap | Meta |
| Header | X-Webhook-Signature | X-Hub-Signature-256 |
| Segredo | whsec_… | App Secret da Meta |
| Formato HMAC | hex puro | sha256=<hex> |
Não aponte o callback do app Meta para este exemplo. A BotoZap já opera esse receiver, persiste o envelope cru antes do 200 e cria a Entrega durável que chega ao seu Endpoint.
Para detalhes do header e do payload, continue em Webhooks.
