Eventos disponíveis
Catálogo dos eventos de webhook do PEPINO ZAP, com o envelope comum e o payload de cada um, campo a campo.
O PEPINO ZAP envia os eventos abaixo por webhook. Esta página é o catálogo de eventos: descreve o envelope comum a todos eles e, para cada evento, o objeto data exato que você recebe — campo a campo, com exemplos reais de payload.
Você cadastra os webhooks via API (POST /v1/webhooks), de forma self-service e escopada ao seu Projeto — veja Webhooks. Para validar que a entrega realmente veio do PEPINO ZAP, consulte Assinatura. Para o comportamento de timeout, reenvio e idempotência, consulte Entrega e retry. Os mesmos eventos e o mesmo envelope também chegam por WebSocket, se você preferir um transporte em tempo real sem expor um endpoint público.
Webhook ≠ histórico. Estes eventos são o push ao vivo — o que acontece no momento. Para
ler o histórico persistido (listar conversas, paginar mensagens, baixar a mídia já hospedada
por nós), use os endpoints do Inbox com a mesma API key — GET /v1/inbox/conversations e
.../conversations/{id}/messages. O PEPINO ZAP é o system of record: você não precisa
armazenar as mensagens que chegam aqui por conta própria. Ver Persistência e
histórico.
| Evento | Quando dispara |
|---|---|
message.received | Uma mensagem com conteúdo chegou em uma instância. |
message.status | Mudou o status de entrega de uma mensagem enviada pela instância (entregue/lida). |
instance.status | A conexão da instância com o WhatsApp mudou de estado. |
call.received | A instância recebeu uma chamada (voz ou vídeo). |
message.deleted | Um contato apagou (revogou) uma mensagem em uma conversa com a instância. |
group.participants | Alguém entrou, saiu, foi promovido ou rebaixado em um grupo. |
group.updated | O nome ou o tópico (descrição) de um grupo mudou. |
contact.updated | O nome de exibição (push name) de um contato mudou. |
label.updated | Uma etiqueta (WhatsApp Business) foi criada, editada ou apagada. |
label.association | Uma etiqueta foi aplicada ou removida de uma conversa. |
campaign.status | Mudou o status de um destinatário de campanha (enviado/entregue/lido/respondeu/falhou/inválido). |
Um webhook só recebe os eventos que assina. Ao criar o webhook (POST /v1/webhooks), o campo
events define a lista — e uma lista vazia significa "todos os eventos". Para receber só
mensagens recebidas, por exemplo, crie o webhook com "events": ["message.received"]. Os nomes
válidos são exatamente os da tabela acima; qualquer outro valor é recusado com 400. Ver
Webhooks.
Envelope comum
Toda entrega usa o método POST e sempre o mesmo envelope. Apenas o conteúdo de data muda de um evento para outro — o que permite escrever um único handler que primeiro lê event e só então interpreta data.
{
"event": "message.received",
"instanceId": "cm5x8a9k20001abc123def456",
"timestamp": "2026-05-29T22:16:30Z",
"data": {}
}Campos do envelope:
| Campo | Tipo | Significado |
|---|---|---|
event | string | Nome do evento — um dos onze da tabela acima (message.received, message.status, instance.status, call.received, message.deleted, group.participants, group.updated, contact.updated, label.updated, label.association, campaign.status). Faça o switch por aqui — não tente adivinhar o tipo pelos campos de data. |
instanceId | string | Identificador da instância que originou o evento (formato cm...). É o mesmo id retornado ao criar a instância. Em um Projeto com vários números, use-o para saber de qual instância veio o evento. |
timestamp | string | Data e hora do evento em ISO 8601 / RFC 3339, em UTC (sufixo Z). Para message.received e message.status, é o horário do evento no WhatsApp; para instance.status, o horário da transição de conexão. |
data | objeto | Os dados específicos do evento. O formato depende de event — detalhado nas seções abaixo. |
Headers presentes em toda entrega:
Content-Type: application/json
User-Agent: pepino-zap-webhooks/1
X-Pepino-Event: message.received
X-Pepino-Webhook-Id: <id-do-webhook>
X-Pepino-Signature: sha256=<hmac-hex>X-Pepino-Event— repete oeventdo corpo. Útil para rotear a entrega antes de fazer parse do JSON.X-Pepino-Webhook-Id— identifica qual webhook cadastrado originou a entrega. É constante por webhook (o mesmo em todas as entregas), então não é uma chave de deduplicação; para deduplicar, use um identificador de negócio emdata(comowaMessageId). Ver Idempotência.X-Pepino-Signature— assinatura HMAC-SHA256 do corpo bruto, no formatosha256=<hmac-hex>. Permite verificar a autenticidade do que foi recebido. O cálculo está descrito em Assinatura.
Calcule e valide a assinatura sobre os bytes crus do corpo, antes de qualquer JSON.parse.
Re-serializar o objeto depois do parse muda espaçamento e ordem de chaves, e a assinatura deixa de
bater. Ver Assinatura.
message.received
Disparado quando a instância recebe uma mensagem com conteúdo.
{
"event": "message.received",
"instanceId": "cm5x8a9k20001abc123def456",
"timestamp": "2026-05-29T22:16:30Z",
"data": {
"from": "5511999999999@s.whatsapp.net",
"type": "image",
"text": "Mensagem de teste",
"waMessageId": "3EB0C767D26A1D8E2B5F",
"mediaUrl": "https://...&X-Amz-Expires=86400",
"mimeType": "image/jpeg"
}
}Campos de data:
| Campo | Tipo | Sempre presente? | Significado |
|---|---|---|---|
from | string | Sim | Remetente da mensagem (ou o grupo de origem). Pode vir em mais de um formato — ver abaixo. |
type | string | Sim | Tipo do conteúdo: text, image, video, audio, document, sticker, location, contact, poll ou unknown. |
text | string | Sim (pode ser "") | Conteúdo textual. Em text, é o corpo. Em image/video/document, é a legenda (vazio se não houver). Em location/contact/poll, é o nome/título do item (nome do local, nome do contato, pergunta da enquete). Em audio/sticker, vem vazio. |
waMessageId | string | Sim | Identificador da mensagem no WhatsApp. Use-o para correlacionar com replies e para deduplicar mensagens repetidas. |
mediaUrl | string | Só em mídia baixada | URL pré-assinada para baixar o arquivo. Presente apenas quando o tipo é de mídia (image/video/audio/document/sticker) e o download foi concluído. Ver o Callout abaixo. |
mimeType | string | Só junto de mediaUrl | Tipo MIME do arquivo (ex. image/jpeg, audio/ogg, application/pdf). Acompanha sempre o mediaUrl. |
O objeto data traz apenas estes campos. Não há pushName, senderPhone, conversationId
nem dados da mensagem citada no payload do webhook — não dependa de campos que não estão listados
aqui.
O campo type em detalhe
O type indica como interpretar o restante do payload:
type | O que é | O que esperar em text | mediaUrl? |
|---|---|---|---|
text | Mensagem de texto (inclui texto com link/citação) | O corpo da mensagem | Não |
image | Imagem | A legenda (se houver) | Sim, quando baixada |
video | Vídeo | A legenda (se houver) | Sim, quando baixada |
audio | Áudio ou nota de voz | Vazio | Sim, quando baixada |
document | Documento/arquivo | A legenda (se houver) | Sim, quando baixada |
sticker | Figurinha | Vazio | Sim, quando baixada |
location | Localização (pin ou ao vivo) | O nome do local (se houver) | Não |
contact | Contato compartilhado (vCard) | O nome exibido do contato | Não |
poll | Enquete | A pergunta da enquete | Não |
unknown | Conteúdo recebido que não se encaixa nos tipos acima, mas ainda traz texto | Pode ter texto | Não |
Trate type como uma lista aberta: faça um switch nos valores que você conhece e tenha um
caso default para o resto. Assim, qualquer tipo novo (ou unknown) é tratado de forma graciosa,
sem quebrar o handler.
O campo from: dois (e às vezes três) formatos
O from identifica a origem e não é sempre um telefone. Ele pode vir em três formatos, distinguíveis pelo sufixo após o @:
- Número com sufixo
@s.whatsapp.net— por exemplo5511999999999@s.whatsapp.net. É o caso mais comum: o trecho antes do@é o telefone em E.164 (sem o+). - LID com sufixo
@lid— por exemplo123456789@lid. Ocorre quando o contato usa o recurso de privacidade do WhatsApp e o número real ainda não foi resolvido. Aqui o valor antes do@lidé um identificador interno, não um telefone. - Grupo com sufixo
@g.us— por exemplo120363012345678901@g.us. Aparece quando a mensagem foi recebida em um grupo. O valor antes do@identifica o grupo, não uma pessoa.
Ao processar from, não assuma que o trecho antes do @ é sempre um telefone. Use o
sufixo para decidir: @s.whatsapp.net = número, @lid = identificador de privacidade,
@g.us = grupo. Tentar discar/normalizar o trecho de um @lid ou @g.us como telefone leva a
dados incorretos.
Mensagens recebidas em grupos geram message.received por padrão (com from terminando em
@g.us). Se você só atende conversas 1:1, ligue a configuração ignoreGroups da instância — com
ela ativa, mensagens de grupo não geram webhook. Ver Configurações da
instância.
Mídia: como baixar o arquivo
Em mensagens de mídia, o binário não vem no payload — vem uma URL pré-assinada em mediaUrl, com validade de 24 horas.
"data": {
"from": "5511999999999@s.whatsapp.net",
"type": "audio",
"text": "",
"waMessageId": "3EB0C767D26A1D8E2B5F",
"mediaUrl": "https://....s3.amazonaws.com/media/...&X-Amz-Expires=86400",
"mimeType": "audio/ogg"
}Baixe o arquivo diretamente pela URL, com um GET simples e sem credenciais adicionais (a autorização já está embutida na própria URL):
curl -L -o arquivo "$MEDIA_URL"mediaUrl só é incluído quando o download da mídia foi concluído pela plataforma. Se o
download não estava pronto no momento do evento, o payload vem sem mediaUrl/mimeType,
apenas com os metadados (from, type, text, waMessageId). Trate a ausência desses campos
com naturalidade — não é um erro. Como a URL expira em 24h, baixe e armazene o arquivo no seu
lado se precisar dele por mais tempo; não guarde a URL para usar depois.
O que não gera message.received
Apenas mensagens com conteúdo exibível geram o evento. Não são entregues:
- Ecos das próprias mensagens que a instância envia (mensagens "de mim").
- Mensagens de controle do WhatsApp: cabeçalho de álbum (vem junto das fotos), reações, edições, exclusões, distribuição de chave de sessão e mensagens de protocolo.
- Status (
status@broadcast), listas de transmissão e canais (newsletters) — não são conversas e não viram evento. - Grupos, quando a configuração
ignoreGroupsda instância está ligada.
message.status
Disparado quando muda o status de entrega de uma mensagem que a instância enviou. Como o envio é assíncrono (a API responde 202 apenas enfileirando a mensagem), este evento é a sua confirmação de entrega e de leitura.
{
"event": "message.status",
"instanceId": "cm5x8a9k20001abc123def456",
"timestamp": "2026-05-29T22:17:00Z",
"data": {
"status": "READ",
"to": "5511999999999@s.whatsapp.net",
"waMessageIds": ["3EB0C767D26A1D8E2B5F", "3EB1A2B3C4D5E6F70819"]
}
}Campos de data:
| Campo | Tipo | Significado |
|---|---|---|
status | string | DELIVERED (entregue no aparelho do destinatário) ou READ (lida — ou, no caso de áudio, ouvida). |
to | string | Destinatário das mensagens, no mesmo formato de JID do from (geralmente ...@s.whatsapp.net; pode ser ...@g.us para grupos). |
waMessageIds | array de string | Lista dos waMessageId afetados por este status. Um único receipt pode cobrir várias mensagens de uma vez — por isso é sempre um array. |
Para correlacionar com o que você enviou, guarde o waMessageId retornado no envio e cruze-o com os waMessageIds recebidos aqui.
O fluxo normal é DELIVERED seguido de READ, mas nem toda mensagem recebe os dois (e nem
necessariamente nessa ordem): se o destinatário nunca abre a conversa, você verá só DELIVERED;
com retries, o mesmo status pode chegar repetido. Trate cada message.status como uma
atualização idempotente do estado da mensagem, e não como um passo obrigatório de uma
sequência. Não há um status de "falhou" neste evento.
instance.status
Disparado quando muda o status de conexão da instância com o WhatsApp.
{
"event": "instance.status",
"instanceId": "cm5x8a9k20001abc123def456",
"timestamp": "2026-05-29T22:18:00Z",
"data": {
"status": "DISCONNECTED",
"jid": "",
"reason": ""
}
}Campos de data:
| Campo | Tipo | Significado |
|---|---|---|
status | string | A transição de conexão. Assume um dos valores da tabela abaixo. |
jid | string | Identificador WhatsApp do número conectado (ex. 5511999999999@s.whatsapp.net). Preenchido quando há um número associado à transição (notadamente em CONNECTED); vem "" caso contrário. |
reason | string | Informação adicional sobre a transição, quando houver (por exemplo, o motivo de um LOGGED_OUT ou o código de um BANNED). Vem "" quando não há detalhe. |
Valores possíveis de status:
status | Significado | O que fazer |
|---|---|---|
CONNECTED | A instância conectou e está operacional. O campo jid traz o identificador WhatsApp do número. | Pode enviar mensagens. |
DISCONNECTED | A conexão caiu. | Nada — a instância reconecta automaticamente. |
LOGGED_OUT | A sessão foi encerrada no aparelho (o usuário removeu o aparelho conectado). | É preciso parear de novo via QR Code. |
STREAM_REPLACED | O número foi aberto em outra sessão; esta conexão foi substituída. | Verifique se o número não está conectado em outro lugar. |
BANNED | O número foi bloqueado pela plataforma do WhatsApp (temporário ou permanente). | Estado terminal — não reconecta; o envio passa a responder 409. O reason traz o código do bloqueio. Ver o detalhe abaixo. |
Este evento cobre apenas as transições acima. O ciclo de pareamento — os estados
CONNECTING e QR_PENDING, e o sucesso de leitura do QR — não é enviado por
instance.status. Acompanhe o pareamento pelo stream de
QR (ou pelo polling) e consulte o estado atual a qualquer
momento com GET /v1/instances/{id}.
Uma vez pareada, a instância reconecta sozinha após quedas, reinícios e deploys, sem novo QR.
Por isso, um DISCONNECTED é normalmente transitório: não acione um novo pareamento por causa
dele. Só LOGGED_OUT exige escanear o QR de novo. Você pode confiar no status: se a sessão
morre — por exemplo, o número é desvinculado no aparelho enquanto a instância está fora do ar e
o LOGGED_OUT não chega na hora — a plataforma reconcilia o estado e emite um instance.status
com DISCONNECTED, em geral em um a dois minutos. Ou seja, o status não fica preso
indefinidamente em um CONNECTED fantasma. Se, mesmo assim, uma instância permanecer em
DISCONNECTED sem voltar para CONNECTED por alguns minutos (confirme com GET /v1/instances/{id}), trate como sessão encerrada e gere um novo
QR Code. Para o ciclo de vida completo, veja Sua primeira
instância.
Sobre o BANNED. Ele cobre dois casos que o WhatsApp trata como bloqueio: um bloqueio
temporário (anti-spam — por exemplo, mensagens demais para quem não tem você na agenda, ou gente
demais te bloqueando) e um banimento permanente da conta. Nos dois o status vai para
BANNED, o envio passa a responder 409 e a instância não reconecta; o reason traz o código
do bloqueio quando o WhatsApp o informa. Há ainda um caso sem evento: um número "sombreado"
(soft-block) pode, em vez de emitir BANNED, simplesmente nunca concluir o pareamento — o QR
fica sendo regerado sem chegar a CONNECTED. O protocolo multi-device do WhatsApp não dá um sinal
determinístico de banimento aí; por isso, trate um número que não pareia depois de várias
tentativas como provavelmente bloqueado, em vez de insistir no mesmo número.
call.received
Disparado quando a instância recebe uma chamada (voz ou vídeo) de um contato — no mesmo instante em que o telefone "tocaria". Útil para registrar tentativas de ligação, alertar um atendente, ou casar com a configuração de rejeitar chamadas: mesmo com a rejeição ligada, o call.received é emitido — a chamada chegou, e só depois foi rejeitada.
{
"event": "call.received",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"from": "5511999999999@s.whatsapp.net",
"callId": "AB12CD34EF56"
}
}| Campo | Tipo | Descrição |
|---|---|---|
from | string | Quem ligou, no mesmo formato JID do from de message.received (a parte antes do @ é o telefone em E.164). |
callId | string | Identificador da chamada no WhatsApp. Útil para correlacionar os eventos da mesma ligação e para deduplicar. |
Este evento sinaliza apenas que chegou uma chamada — não há eventos de "atendida", "encerrada"
ou duração. Se você quer que o número nunca receba chamadas, ligue rejectCalls (e
opcionalmente rejectCallMessage) nas configurações da
instância.
message.deleted
Disparado quando um contato apaga para todos (revoga) uma mensagem em uma conversa com a instância — o "Apagar mensagem → Apagar para todos" do WhatsApp. Serve para manter o seu CRM/registro fiel: ao receber este evento, marque como apagada (ou esconda) a mensagem correspondente no seu sistema.
{
"event": "message.deleted",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"from": "5511999999999@s.whatsapp.net",
"waMessageId": "3EB0C767D26A1D8E2B5F"
}
}| Campo | Tipo | Descrição |
|---|---|---|
from | string | A conversa onde a mensagem foi apagada (JID do contato ou do grupo), no mesmo formato do from de message.received. |
waMessageId | string | O waMessageId da mensagem apagada — cruze com o waMessageId recebido em message.received para localizar qual mensagem revogar no seu lado. |
Refere-se a apagar para todos (revogação), que é o que se propaga pelo WhatsApp — "Apagar para
mim" é local no aparelho de quem apagou e não gera evento. Respeita o ignoreGroups: se você
ignora grupos, uma revogação em grupo também não dispara.
group.participants
Disparado quando a composição de um grupo muda: alguém entrou, saiu (ou foi removido), foi promovido a admin ou rebaixado. Só vale para grupos dos quais a instância participa.
{
"event": "group.participants",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"groupJid": "120363012345678901@g.us",
"by": "5511999999999@s.whatsapp.net",
"joined": ["5511888888888@s.whatsapp.net"],
"left": [],
"promoted": [],
"demoted": []
}
}| Campo | Tipo | Descrição |
|---|---|---|
groupJid | string | O grupo (...@g.us). |
by | string | Quem fez a mudança ("" se o WhatsApp não informou). |
joined / left / promoted / demoted | array de string | Os participantes afetados (JIDs). Em geral só um desses arrays vem preenchido por evento. |
group.updated
Disparado quando os metadados de um grupo mudam — o nome ou o tópico (descrição).
{
"event": "group.updated",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"groupJid": "120363012345678901@g.us",
"by": "5511999999999@s.whatsapp.net",
"name": "Novo nome do grupo"
}
}| Campo | Tipo | Descrição |
|---|---|---|
groupJid | string | O grupo. |
by | string | Quem fez a mudança. |
name | string | Presente só se o nome mudou — o novo nome. |
topic | string | Presente só se o tópico/descrição mudou — o novo texto. |
contact.updated
Disparado quando o nome de exibição (push name) de um contato muda — percebido ao receber uma mensagem dele com um nome diferente do que estava em cache.
{
"event": "contact.updated",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"from": "5511999999999@s.whatsapp.net",
"oldName": "João",
"newName": "João Silva"
}
}| Campo | Tipo | Descrição |
|---|---|---|
from | string | O contato. |
oldName | string | O nome anterior (em cache). |
newName | string | O novo nome de exibição. |
Refere-se ao push name que o próprio contato definiu — não à foto de perfil, e não ao nome que você salvou na sua agenda.
label.updated
Disparado quando uma etiqueta (recurso do WhatsApp Business) é criada, renomeada ou apagada.
{
"event": "label.updated",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"labelId": "5",
"name": "Cliente VIP",
"color": 3,
"deleted": false
}
}| Campo | Tipo | Descrição |
|---|---|---|
labelId | string | Identificador da etiqueta. |
name | string | Nome atual da etiqueta. |
color | number | Índice de cor da etiqueta no WhatsApp. |
deleted | boolean | true se a etiqueta foi apagada. |
label.association
Disparado quando uma etiqueta é aplicada ou removida de uma conversa.
{
"event": "label.association",
"instanceId": "inst_123",
"timestamp": "2026-06-06T12:00:00Z",
"data": {
"labelId": "5",
"chat": "5511999999999@s.whatsapp.net",
"labeled": true
}
}| Campo | Tipo | Descrição |
|---|---|---|
labelId | string | A etiqueta. |
chat | string | A conversa (contato ou grupo). |
labeled | boolean | true = aplicada; false = removida. |
Etiquetas (label.*) são um recurso do WhatsApp Business — só fazem sentido se o número
conectado é uma conta Business que usa etiquetas. Os eventos do sync inicial são ignorados;
você recebe só as mudanças em tempo real.
campaign.status
Disparado a cada mudança de status de um destinatário de uma campanha de disparo em massa. É o acompanhamento em tempo real do disparo, por destinatário — complementar ao agregado de GET /v1/campaigns/{id}.
{
"event": "campaign.status",
"instanceId": "cm5x8a9k20001abc123def456",
"timestamp": "2026-06-18T14:32:00Z",
"data": {
"campaignId": "camp_3Hk9Zq2pVxL",
"recipient": "5511999999999",
"name": "Maria",
"status": "delivered",
"error": null
}
}Campos de data:
| Campo | Tipo | Significado |
|---|---|---|
campaignId | string | A campanha a que o destinatário pertence. |
recipient | string | O número (apenas dígitos) do destinatário. |
name | string | Nome do destinatário (variável nome), se informado. Pode vir ausente. |
status | string | Estado: sent, delivered, read, responded, failed ou invalid. |
error | string | null | Motivo, quando failed ou invalid (ex.: número não está no WhatsApp). |
O ciclo típico de um destinatário é sent -> delivered -> read, e responded se o contato responder. invalid sai antes do envio (número sem WhatsApp, quando validateNumbers está ligado) e não consome o teto diário; failed é uma falha real de envio. Trate cada evento como uma atualização idempotente do estado daquele destinatário — nem todos os estados ocorrem para todos, e a ordem pode variar.
Erros e situações de borda
Estes eventos são entregues ao seu endpoint; eles não retornam códigos de erro para você. O que você precisa observar do lado do receptor:
- Responda
2xxrápido. Qualquer resposta fora de2xx, timeout (15s) ou erro de rede faz a entrega falhar e ser reenviada com backoff, até 6 tentativas. Ver Entrega e retry. - Deduplique. Por causa do retry, o mesmo evento pode chegar mais de uma vez. Use um identificador de negócio em
data—waMessageIdemmessage.received,waMessageIdsemmessage.status— como chave de idempotência. OX-Pepino-Webhook-Idé constante por webhook e não serve para isso. - Valide a assinatura. Trate como inválida (e descarte) qualquer entrega cujo
X-Pepino-Signaturenão bata com o HMAC do corpo bruto. Ver Assinatura. - Tolere campos ausentes e tipos novos. Em
message.received,mediaUrl/mimeTypepodem não vir;textpode ser"". Eminstance.status,jid/reasonpodem ser"". Tratetypeestatuscomo listas abertas, com um casodefault.
Os erros de cadastro de webhook (criar/listar/remover via POST /v1/webhooks) — como 400 para evento inválido ou URL recusada, 401 para key ausente/inválida — estão documentados em Webhooks.
Próximos passos
- Webhooks — como cadastrar webhooks via API e gerenciar o secret.
- Assinatura — como validar
X-Pepino-Signature(com exemplos em Node, Python e Go). - Entrega e retry — timeout, critério de sucesso e política de reenvio.
- WebSocket — receber os mesmos eventos em tempo real, sem endpoint público.
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.
Assinatura HMAC
Como validar que a entrega de webhook veio mesmo do PEPINO ZAP, recalculando o HMAC-SHA256 do corpo bruto com o secret whsec_ e comparando com o header X-Pepino-Signature.