PEPINO ZAPdocs
Webhooks

Webhooks

Receba eventos do PEPINO ZAP no seu endpoint em tempo real. Cadastre webhooks self-service via POST /v1/webhooks, com entrega assinada (HMAC), secret whsec_ exibido uma vez e política de retry.

Ver como Markdown

Um webhook é uma notificação que o PEPINO ZAP envia ao seu servidor quando algo acontece em uma instância — uma mensagem chega, o status de entrega de uma mensagem enviada muda, ou a conexão da instância muda de estado. Em vez de o seu sistema ficar consultando a API repetidamente para saber "chegou algo novo?", o seu endpoint recebe um POST assinado a cada evento, no momento em que ele acontece.

A URL do webhook é a sua — o PEPINO ZAP não gera nenhuma URL. Aqui o modelo é o de Stripe/Twilio, não o de Slack/Discord. No Slack/Discord você cria um "incoming webhook" e recebe uma URL pronta para postar; aqui é o inverso: você expõe um endpoint público no seu servidor (ex.: https://seusistema.com/webhook/pepinozap) e cadastra a URL dele no campo url; o PEPINO ZAP faz POST nesse endereço a cada evento. Não existe nenhuma URL gerada por nós para você consumir — quem fornece a URL é você.

Esta página é a visão geral: o que é um webhook, como cadastrá-lo via API (self-service), o que cada chamada retorna, os erros possíveis e quando preferir webhook ou WebSocket. O detalhe de cada parte vive nas páginas de Eventos, Assinatura e Entrega e retry.

Todas as requisições de gerenciamento (cadastrar, listar, remover) exigem o header Authorization: Bearer <API_KEY>. Os exemplos usam a URL de produção https://api.pepinozap.com.br com uma key pzk_live_; no modo teste, use https://sandbox.pepinozap.com.br com uma key pzk_test_. O host decide o modo, e a key precisa casar com o host — pzk_live_ em api.*, pzk_test_ em sandbox.*; usar a key no host errado retorna 401. Ver Autenticação.

Como funciona, em três passos

  1. Uma instância gera um evento — message.received (mensagem recebida), message.status (entregue/lida) ou instance.status (conexão mudou).
  2. A plataforma envia um POST assinado para a URL que você cadastrou, com o corpo do evento e os headers de identificação e assinatura.
  3. O seu backend lê o corpo, valida a assinatura e responde com 2xx para confirmar o recebimento. Se você não responder 2xx (ou demorar mais que o timeout), a entrega é reenviada.

Cada entrega traz três headers, sempre:

HeaderConteúdo
X-Pepino-EventNome do evento da entrega (ex.: message.received).
X-Pepino-Webhook-IdIdentificador do webhook cadastrado (a configuração, ex.: wh_abc123). É constante em todas as entregas daquele mesmo webhook — não é um id por entrega nem um número de sequência. Para deduplicar (o mesmo evento pode chegar mais de uma vez), use um identificador de negócio presente em data, como waMessageId.
X-Pepino-Signaturesha256=<hmac-hex> — o HMAC-SHA256 do corpo bruto da requisição, calculado com o secret do webhook.

A assinatura é o que prova que a entrega veio do PEPINO ZAP (e não foi adulterada). Validá-la é fortemente recomendado antes de processar qualquer entrega — veja Assinatura.

O envelope de cada entrega

Independentemente do evento, o corpo do POST tem sempre o mesmo formato de envelope. O que muda entre os eventos é só o conteúdo de data:

{
  "event": "message.received",
  "instanceId": "cm5x8a9k20001abc123def456",
  "timestamp": "2026-05-30T12:00:00Z",
  "data": {}
}
  • event — o nome do evento (message.received, message.status ou instance.status).
  • instanceId — a instância que originou o evento. Um webhook é escopado ao Projeto, então recebe eventos de todas as instâncias do Projeto; use este campo para saber de qual.
  • timestamp — data e hora do evento em ISO 8601 (UTC).
  • data — o objeto específico do evento. O formato de cada um está em Eventos.

Cadastrando um webhook

Você cadastra seus webhooks via API, de forma self-service, escopados ao seu Projeto — não depende do operador. Faça um POST /v1/webhooks com três campos:

curl -X POST https://api.pepinozap.com.br/v1/webhooks \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "meu-endpoint",
    "url": "https://meusistema.com/webhook/pepinozap",
    "events": []
  }'

O corpo da requisição

CampoTipoObrigatórioO que é
namestringSimUm rótulo livre para você identificar o webhook (ex.: producao-crm). Espaços nas pontas são removidos; vazio é rejeitado com 400. Nomes podem repetir — não há checagem de duplicidade.
urlstringSimA URL pública do seu endpoint que vai receber os POST. Precisa ser http/https absoluta. Recomenda-se https.
eventsarray de stringsNãoQuais eventos este webhook deve receber. Array vazio ([]) ou omitido = todos os eventos. Para filtrar, liste só os que quer (ex.: ["message.received"]).

Os valores aceitos em events são estes:

ValorQuando dispara
message.receivedUma mensagem chegou em uma instância.
message.statusMudou o status de entrega de uma mensagem enviada (entregue/lida).
instance.statusA conexão de uma instância mudou (conectou, caiu, deslogou, foi banida).
call.receivedUma chamada (voz/vídeo) foi recebida.
message.deletedO remetente apagou/revogou uma mensagem.
group.participantsAlguém entrou/saiu ou foi promovido/rebaixado em um grupo.
group.updatedNome ou descrição de um grupo mudou.
contact.updatedO nome de exibição de um contato mudou.
label.updatedUma etiqueta (Business) foi criada, editada ou apagada.
label.associationUma etiqueta foi aplicada ou removida de uma conversa.

Qualquer string fora dessa lista faz a chamada falhar com 400 e a mensagem evento inválido: <valor>. O payload dos principais está em Eventos.

A url é validada na criação: precisa ter esquema http ou https e ter um host. Se você apontar para um endereço IP interno/privado literal (loopback como 127.0.0.1, faixas privadas como 10.x/192.168.x, link-local etc.), a criação é rejeitada com 400 — é uma proteção anti-SSRF. Para testar localmente, exponha o endpoint com um túnel (por exemplo, um serviço de tunelamento HTTP) e cadastre a URL pública dele, não localhost.

A resposta: 201 Created com o secret

A criação retorna 201 Created com o webhook recém-criado e o secret:

{
  "id": "wh_abc123",
  "name": "meu-endpoint",
  "url": "https://meusistema.com/webhook/pepinozap",
  "events": [],
  "enabled": true,
  "lastSuccessAt": null,
  "lastFailureAt": null,
  "lastFailureReason": null,
  "createdAt": "2026-05-30T12:00:00Z",
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
  • id — o identificador do webhook. Guarde-o: é o que você usa para remover o webhook depois (DELETE /v1/webhooks/{id}).
  • enabled — webhooks nascem habilitados (true); já começam a receber entregas. Você pode pausar e retomar a entrega depois sem perder a config (ver Pausar e retomar).
  • lastSuccessAt, lastFailureAt, lastFailureReason — telemetria da última entrega bem-sucedida e da última falha. Vêm null enquanto não houve entrega; depois ajudam a diagnosticar um endpoint com problema (veja Entrega e retry).
  • secret — a chave para validar a assinatura das entregas. Detalhado a seguir.

O secret whsec_ é mostrado uma única vez

O campo secret, com prefixo whsec_, só aparece nesta resposta de criação. Ele não volta a ser exibido em nenhuma outra chamada — GET /v1/webhooks não o retorna. É com esse valor que você recalcula o HMAC e confere o header X-Pepino-Signature de cada entrega (ver Assinatura).

Copie o secret agora e guarde-o em variável de ambiente ou cofre de segredos (fora do código-fonte). Se você perder o secret, não há como recuperá-lo: a única saída é remover o webhook e criar outro, o que gera um secret novo — e, a partir daí, você passa a validar as assinaturas com o secret novo. Cada webhook tem o seu próprio secret.

Variações de cadastro

Recebendo só um tipo de evento. Liste apenas o que interessa em events:

curl -X POST https://api.pepinozap.com.br/v1/webhooks \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "so-recebidas", "url": "https://meusistema.com/in", "events": ["message.received"] }'

Vários endpoints. Você pode cadastrar mais de um webhook no mesmo Projeto — por exemplo, um endpoint para mensagens e outro para status de conexão. Cada um tem URL, eventos e secret próprios, e cada um recebe sua própria entrega assinada.

Listando seus webhooks

Use GET /v1/webhooks para ver os webhooks cadastrados no Projeto:

curl https://api.pepinozap.com.br/v1/webhooks \
  -H "Authorization: Bearer $YOUR_API_KEY"

A resposta é 200 OK com um array em items:

{
  "items": [
    {
      "id": "wh_abc123",
      "name": "meu-endpoint",
      "url": "https://meusistema.com/webhook/pepinozap",
      "events": [],
      "enabled": true,
      "lastSuccessAt": "2026-06-05T11:59:00Z",
      "lastFailureAt": null,
      "lastFailureReason": null,
      "createdAt": "2026-05-30T12:00:00Z"
    }
  ]
}

Repare que a listagem não inclui o secret — esse campo só existe na resposta de criação. Use lastSuccessAt / lastFailureAt / lastFailureReason para auditar a saúde de cada endpoint: um lastFailureReason preenchido e um lastSuccessAt antigo indicam que o seu endpoint vem rejeitando ou não respondendo às entregas.

A listagem respeita o modo da key: uma key pzk_test_ (host sandbox.*) lista os webhooks de teste; uma key pzk_live_ (host api.*) lista os de produção. São conjuntos separados.

Removendo um webhook

Para parar de receber entregas em um endpoint, remova o webhook pelo id retornado na criação (ou obtido na listagem):

curl -X DELETE https://api.pepinozap.com.br/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer $YOUR_API_KEY"

Em sucesso, retorna 204 No Content (sem corpo). Se o id não existir, não for do seu Projeto, ou for de outro modo (teste vs. produção), retorna 404 com webhook não encontrado neste projeto.

A remoção é definitiva e invalida o secret daquele webhook. Para apenas interromper temporariamente as entregas (sem perder a config), use o pause abaixo em vez de remover. Já para trocar URL ou eventos não há edição: o fluxo é remover e criar de novo com os novos parâmetros (o que gera um secret novo) — atualize a configuração do seu endpoint antes de descartar o antigo.

Pausar e retomar

Às vezes você precisa parar de receber as entregas por um tempo — manutenção do seu endpoint, um deploy, ou investigar um problema — sem perder a configuração (URL, secret e eventos). Para isso, alterne o campo enabled com PATCH /v1/webhooks/{id}:

# pausar a entrega
curl -X PATCH https://api.pepinozap.com.br/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

# retomar
curl -X PATCH https://api.pepinozap.com.br/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'

Retorna 200 OK com o webhook atualizado. Enquanto enabled for false, nenhuma entrega é feita para aquele endpoint (os eventos nem chegam a ser enfileirados para ele); ao voltar para true, as entregas recomeçam a partir dos próximos eventos — o que ocorreu durante a pausa não é entregue retroativamente.

A vantagem sobre remover: pausar mantém o mesmo id e o mesmo secret — você religa quando quiser, sem reconfigurar nada do seu lado nem trocar a chave de assinatura. Remover, ao contrário, é definitivo e invalida o secret. No painel (Integrações → Webhooks), esse mesmo controle é o botão Pausar / Retomar.

Entrega e idempotência (resumo)

A plataforma envia cada entrega via POST e espera uma resposta 2xx rápida. O comportamento completo está em Entrega e retry; o essencial:

  • Timeout de 15 s por tentativa. Responda 2xx em poucos segundos e processe o evento de forma assíncrona.
  • Sucesso = 2xx. Qualquer 4xx/5xx, timeout ou erro de rede conta como falha.
  • Retry com backoff exponencial, até 6 tentativas. Por isso, o mesmo evento pode chegar mais de uma vez: trate as entregas de forma idempotente, deduplicando por um identificador de negócio presente no data (por exemplo, waMessageId). O header X-Pepino-Webhook-Id identifica o webhook cadastrado (é constante por webhook), então não serve sozinho para deduplicar eventos.

Erros possíveis no cadastro

O envelope de erro é { "error": { "code", "message" } }.

CódigoSignificado
400Corpo inválido. Casos: JSON malformado (JSON inválido: ...); name ausente (nome obrigatório); url inválida — esquema diferente de http/https, sem host, ou apontando para IP interno/privado (URL: ...); evento não suportado em events (evento inválido: <valor>).
401API key ausente, inválida, ou usada no host do modo errado (pzk_live_ em sandbox.* ou vice-versa). Ver Autenticação.
403Projeto inativo ou sem permissão.
404(No DELETE) webhook não encontrado neste Projeto — id inexistente, de outro Projeto, ou de outro modo.
500Erro interno ao gerar o secret ou ao persistir o webhook.

Webhook ou WebSocket?

Os dois transportes entregam exatamente os mesmos eventos, com o mesmo envelope. A escolha é sobre a garantia de entrega que você precisa:

WebhookWebSocket
Endpoint públicoNecessárioNão precisa
EntregaCom retry e backoff, até 6 tentativasSó enquanto o cliente está conectado (sem buffer/retry)
Indicado paraIntegrações de backend que exigem garantia de entregaFrontends, dashboards, protótipos

Se você não tem um endpoint público — por exemplo, num frontend ou protótipo — pode receber os mesmos eventos por uma conexão WebSocket, sem expor uma URL. O webhook continua sendo a opção recomendada quando você precisa de entrega garantida. Os dois podem ser usados ao mesmo tempo: o WebSocket não substitui o webhook quando você precisa de durabilidade.

Próximos passos

  • Eventos — os três eventos disponíveis e o payload de cada um.
  • Assinatura — como validar X-Pepino-Signature com o secret whsec_, com exemplos em Node.js, Python e Go.
  • Entrega e retry — a política de timeout, sucesso/falha e reenvio em detalhe.
  • WebSocket — receber os eventos em tempo real sem endpoint público.

On this page