Skip to main content
Um evento é o registro do que aconteceu na sua conta: uma fatura paga, uma cobrança recusada, um checkout concluído. O evento existe assim que o fato acontece, com o corpo congelado naquele instante. O webhook é como esse evento chega até você. Você cadastra uma URL, marca os tipos que quer receber, e o Simplo faz um POST assinado para lá. Os dois são coisas separadas de propósito. Se a entrega falhar, o evento continua existindo: você consulta em GET /api/v1/events e reenvia quando o seu servidor voltar.

Configurar

1

Crie uma rota no seu app

Uma rota HTTP pública que aceita POST com JSON, em qualquer linguagem.
2

Cadastre o endpoint no painel

Em Configurações → Webhooks, clique em Novo endpoint e cole a URL. Em produção ela precisa ser https://: o corpo leva o nome e o e-mail do seu cliente, a assinatura prova quem mandou, e o TLS é o que impede que alguém leia isso no caminho. No sandbox http:// também vale, que é o que faz um túnel para a sua máquina funcionar.O endereço também precisa ser da internet pública. Uma URL que resolve para a rede interna — 127.0.0.1, 10.x.x.x, 192.168.x.x, 169.254.169.254 — é recusada no cadastro, e recusada de novo na hora de cada entrega, caso o DNS passe a responder isso depois. A recusa aparece na lista de entregas do endpoint com o motivo.Cada conta cadastra até 5 endpoints. Cada um tem a sua URL, o seu filtro de eventos e o seu segredo de assinatura, então dá para mandar cobranças para um serviço e assinaturas para outro.
3

Marque os eventos

Um endpoint sem nenhum tipo marcado não recebe nada. Marque só o que você vai processar.
4

Copie o segredo e verifique a assinatura

O segredo aparece no card do endpoint, no formato whsec_.... Ele é a chave que valida que o POST veio mesmo do Simplo. Veja Verificar a assinatura.
O botão Testar manda um POST de verdade, assinado com o segredo daquele endpoint, com "type": "webhook.test" e data.object nulo. Ele serve para conferir o caminho de rede e a sua verificação. O evt_ desse POST não vira evento: não procure por ele em GET /api/v1/events.

Estrutura do payload

O corpo é o próprio evento. data.object carrega um recurso, no mesmo formato em que o endpoint dele devolve — a fatura que chega no seu servidor é byte a byte a fatura de GET /api/v1/invoices/{id}.
  • id — o identificador do evento. É o mesmo valor do cabeçalho webhook-id e a sua chave de idempotência.
  • type — o que aconteceu.
  • created — timestamp Unix em segundos do instante do fato.
  • live_modetrue em produção, false no sandbox.
  • data.object — o recurso. Recursos relacionados vêm como ID (customer, subscription, payment_intent), e você busca cada um pelo endpoint dele quando precisar.
O corpo é congelado quando o fato acontece. Uma retentativa três dias depois entrega o que era verdade na hora, não o estado atual do recurso. Se você precisa do estado atual, leia o recurso pela API usando o ID que veio em data.object.id.

Eventos disponíveis

O schema de cada um está na referência da API.
Para liberar acesso depois de um pagamento, use invoice.paid. Ele chega para toda fatura liquidada, tenha a origem sido um checkout hospedado ou a cobrança recorrente de uma assinatura.
Os eventos de assinatura descrevem mudanças diferentes. subscription.overdue sai uma vez por episódio de atraso; receber o pagamento e limpar esse atraso não gera outro evento de ciclo de vida. subscription.reactivated significa que uma assinatura suspensa voltou para pending; o pagamento seguinte gera subscription.activated. subscription.period_started sai para cada ciclo novo, inclusive o primeiro, e uma repetição que encontra o ciclo existente não gera uma segunda cópia. Todos carregam a representação pública da assinatura em data.object. Campos internos usados pela régua de cobrança, como as datas privadas de atraso, suspensão e cancelamento, não fazem parte desse corpo.

Verificar a assinatura

Toda entrega vai assinada no padrão Standard Webhooks, com três cabeçalhos:
A assinatura é um HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{corpo}, em base64. A chave são os bytes do segredo depois do prefixo whsec_, decodificados de base64. Como o timestamp entra no conteúdo assinado, uma requisição capturada não pode ser reenviada depois: recuse entregas com mais de 5 minutos de diferença do seu relógio.

Com a gem standardwebhooks

O verify cuida da tolerância de tempo e da comparação em tempo constante, e devolve o corpo já decodificado.

Sem a gem

Verifique sobre o corpo cru, exatamente como chegou. Reserializar o JSON muda os bytes e a assinatura deixa de conferir.
Bibliotecas oficiais existem para outras linguagens e verificam o mesmo cabeçalho.

Rotacionar o segredo

Rotacionar no card do endpoint gera um segredo novo. Pelas 24 horas seguintes, o cabeçalho webhook-signature traz duas assinaturas, uma por segredo, separadas por espaço. Aceite a entrega se qualquer uma conferir, e você troca o segredo no seu lado sem perder nenhum evento no meio do deploy. Depois das 24 horas, só o novo assina.

Entrega

Responda 2xx rápido

O Simplo espera até 15 segundos pela resposta, com 5 segundos de timeout de conexão. Só 2xx conta como entregue: um 3xx é a sua URL dizendo que mudou de lugar, e é você quem conserta isso no cadastro. A regra é guardar e enfileirar. Grave o corpo, responda 200, processe em job de background. Processar dentro da request é o que faz uma entrega estourar o timeout num dia de pico.

Retentativa: 10 tentativas em 93 horas

Uma entrega que falha é retentada. São dez tentativas no total, densas no começo, porque a maioria das falhas é um deploy ou um restart, e depois diárias, porque o que sobrevive a seis horas é uma indisponibilidade que precisa de conserto. Cada tentativa aparece no painel, no card do endpoint, com o código de resposta, a duração e quando é a próxima.

Deduplique pelo webhook-id

Retentativa significa que o mesmo evento pode chegar duas vezes — por exemplo, quando o seu servidor processou o POST mas demorou 16 segundos para responder. O evt_ é sempre o mesmo, então guarde-o com índice único e trate a segunda chegada como sucesso.
A ordem também não é garantida. invoice.paid pode chegar antes de invoice.created se a primeira entrega tiver falhado. Use created para ordenar, ou leia o recurso pela API quando a ordem importar.

Saúde é informação, não interruptor

Falhas consecutivas marcam o endpoint como instável no painel. Isso não pausa nada: os próximos eventos continuam sendo enviados, e cada um roda a escada inteira de dez tentativas. Um endpoint que passa dias sem aceitar nenhuma entrega é um endereço que não existe mais, e aí sim o Simplo age — sempre avisando antes:
  • 3 dias sem nenhuma entrega aceita: e-mail de aviso.
  • 6 dias: segundo e-mail.
  • 7 dias: o endpoint é desativado, e você reativa no painel quando o endereço voltar.
Uma única entrega aceita zera o relógio.

Reconciliar o que não chegou

Os eventos ficam consultáveis por 30 dias, tenham sido entregues ou não.
O filtro delivery_status responde à pergunta que interessa: GET /api/v1/events/{id} devolve o mesmo corpo que a entrega carregou, o que permite conferir o que você guardou contra o que o Simplo mandou.

Reenviar

O reenvio manda o mesmo corpo, com o mesmo evt_, para os endpoints ativos que assinam aquele tipo agora — inclusive um endpoint cadastrado depois do fato. Cada reenvio abre uma escada nova de dez tentativas. São até 3 reenvios por evento, a qualquer momento dentro dos 30 dias. O botão também está no painel, na lista de entregas de cada endpoint.

Testar localmente

Exponha o seu servidor local com ngrok ou cloudflared:
Gere uma cobrança no sandbox e pague: invoice.paid sai para os seus endpoints com live_mode: false. O botão Testar não substitui isso — ele confere o caminho e a assinatura, e um evento de verdade confere o seu processamento.