O que você precisa garantir no seu endpoint
- Aceitar
POSTem uma URL HTTPS. - Verificar a assinatura antes de processar o corpo.
- Responder com um status 2xx em até 20 segundos.
Como a entrega funciona
A entrega é desacoplada do evento. Quando algo acontece (um pagamento confirma, uma assinatura é criada), a Chargefy grava o evento e enfileira a entrega; um worker dedicado drena a fila e faz oPOST. Isso garante que um endpoint lento ou fora do ar nunca trava a operação que originou o evento.
| Etapa | O que acontece |
|---|---|
| 1. Evento | Uma mudança em recurso público gera um evento com id (evt_...) e payload congelado naquele instante. |
| 2. Enfileiramento | O evento é gravado e enfileirado uma vez por endpoint inscrito naquele tipo. |
| 3. Entrega | Um worker drena a fila a cada minuto, monta os headers assinados e faz o POST para a URL do endpoint. |
| 4. Resultado | Resposta 2xx marca a entrega como concluída. Qualquer outra coisa agenda uma reentrega. |
Cada endpoint inscrito recebe seu próprio evento, com
id próprio. Se você tem dois endpoints ouvindo payment_intent.succeeded, o mesmo fato gera dois eventos independentes — cada um com seu ciclo de entrega e retry.Parâmetros de entrega
| Parâmetro | Valor | Origem |
|---|---|---|
| Timeout por tentativa | 20 segundos | Conexão abortada após esse limite. |
| Critério de sucesso | status 2xx (200–299) | 3xx, 4xx, 5xx e timeout contam como falha. |
| Máximo de tentativas | 10 | Inclui a primeira tentativa. |
| Cadência de reentrega | ~1 minuto entre tentativas | O worker roda a cada minuto; um evento que falhou volta a ser elegível no próximo ciclo. |
| Janela total de retry | ~10 minutos | Depois disso a entrega vira falha permanente. |
| Método HTTP | POST | Sempre. |
| Content-Type | application/json | Corpo é o JSON do evento. |
Retenção do payload
O payload do evento é mantido por 30 dias. Depois disso ele é esvaziado automaticamente (archival), mas os metadados da entrega (status, data, HTTP code, corpo de resposta truncado) permanecem para auditoria.Reentrega manual só é possível enquanto o payload existe. Após o archival de 30 dias não há corpo para reenviar — a tentativa retorna erro indicando que o payload foi arquivado.
O envelope do evento
O corpo entregue é sempre o envelope canônico de evento. O recurso afetado vive emdata.object, no mesmo shape público retornado pela API. As chaves de todo objeto JSON público seguem a ordem canônica: id, object, depois alfabética.
| Campo | Tipo | Descrição |
|---|---|---|
id | string | ID único do evento. É idêntico ao header webhook-id — use-o para idempotência. |
object | string | Sempre "event". |
created_at | string | ISO 8601 do momento em que o evento foi criado. |
data.object | object | Objeto público completo no estado atual. Mesmo DTO do GET single. |
data.previous_attributes | object | Só em eventos de atualização. Contém apenas os campos alterados, com o valor anterior. Ausente em eventos de criação. |
livemode | boolean | true em produção, false em teste. |
organization | string | Organização que originou o evento. Veja fan-out de plataforma. |
request.id | string | Request (req_*) que originou o evento, quando disponível. |
type | string | Tipo do evento (<recurso>.<ação>). Catálogo em Eventos. |
Assinatura
Cada requisição carrega os headers abaixo. A assinatura cobre oid, o timestamp e o corpo bruto — qualquer re-serialização do corpo invalida a assinatura.
| Header | Descrição | Exemplo |
|---|---|---|
webhook-id | ID único do evento. Igual ao id do corpo. | evt_123 |
webhook-timestamp | Unix timestamp em segundos de quando a entrega foi assinada. | 1747405767 |
webhook-signature | Uma ou mais assinaturas, separadas por espaço. Cada uma no formato v1,<base64>. | v1,K7gNU3sdo+OL0wNh... |
content-type | Sempre application/json. | application/json |
user-agent | Identifica o emissor. | chargefy.io webhooks |
Formato do secret
Cada endpoint tem um secret no formato canônico:whsec_) são a chave do HMAC. Esse é exatamente o ponto que garante compatibilidade com as bibliotecas Standard Webhooks: você passa o secret inteiro, com prefixo, e a lib cuida da decodificação.
Algoritmo de assinatura
A string assinada é a concatenação de três partes separadas por ponto:| Componente | Valor |
|---|---|
| Algoritmo | HMAC com SHA-256. |
| Chave | Bytes decodificados do base64 do secret (não a string crua). |
| Mensagem | ${webhook-id}.${webhook-timestamp}.${raw_body} — IDs e timestamp como strings, corpo exatamente como recebido. |
| Saída no header | v1, + assinatura em base64. |
Verificação
A forma mais simples e segura é usar uma biblioteca compatível com Standard Webhooks: ela decodifica o secret, valida a janela de timestamp, faz comparação constant-time e suporta múltiplas assinaturas durante rotação. Existe implementação oficial em Node, Python, Ruby, Go, PHP, Java, C# e Rust.Verificação manual
Se preferir não usar uma biblioteca, o algoritmo é direto:Valide a janela de tempo
Rejeite se
webhook-timestamp estiver fora de ±5 minutos do horário atual. Isso bloqueia replay de requisições antigas capturadas.Monte a string assinada
signed = ${webhook-id}.${webhook-timestamp}.${raw_body}, usando o corpo bruto exatamente como recebido.Decodifique o secret
Remova o prefixo
whsec_ e decodifique o restante de base64. Esses bytes são a chave do HMAC.Verificação manual (TypeScript / Web Crypto)
Rotação de secret e múltiplas assinaturas
O headerwebhook-signature pode conter mais de uma assinatura, separadas por espaço:
Gere o novo secret
Em Configurações → Webhooks, use Resetar secret. O endpoint passa a assinar com o novo secret.
Idempotência
Reentregas e reenvios manuais significam que o mesmo evento pode chegar mais de uma vez. Trate a entrega como at-least-once: garanta que processar o mesmo evento duas vezes não duplica efeitos no seu sistema. O campoid do envelope (igual ao header webhook-id) é estável por evento. Use-o como chave de deduplicação:
Pule duplicatas
Se o
id já existe, responda 200 e não reprocesse — a Chargefy já considera a entrega concluída.Fan-out para plataformas
Quando uma organização conectada a uma plataforma origina um evento, a entrega vai para:- Os endpoints da própria organização que originou o evento (se ela tiver algum inscrito).
- Os endpoints de cada plataforma ativa que tem essa organização como conectada.
organization aponta para a organização que originou o evento (a organização conectada filha), nunca para a organização dona da plataforma.
Isso permite que a plataforma identifique de qual organização conectada o evento veio lendo
organization, sem precisar de nenhum campo extra de relacionamento. Cada fan-out gera um evento com id próprio, então a idempotência continua valendo por destinatário.Reentrega manual
Se um endpoint ficou fora do ar e o evento esgotou as tentativas automáticas — ou se você só quer reprocessar um evento — é possível reenviar manualmente pelo dashboard, em Configurações → Webhooks → Entregas.| Característica | Comportamento |
|---|---|
| Disponibilidade | Enquanto o payload existir (até 30 dias após o evento). |
| Eventos elegíveis | Tanto os que falharam quanto os que já tiveram sucesso. |
| Endpoint removido | Não é possível reenviar — não há destino. |
| Histórico | Um reenvio que falha não apaga um sucesso anterior; cada tentativa registra sua própria entrega. |
Desenvolvimento local
Para testar webhooks na sua máquina, exponha a porta local com um tunnel HTTPS e cadastre a URL gerada como endpoint:Boas práticas
Responda rápido (menos de 20s)
Responda rápido (menos de 20s)
Verifique a assinatura, persista o evento e responda
200 imediatamente. Processe a lógica pesada em background. Acima de 20 segundos a entrega é considerada falha e reentregue.Verifique a assinatura sempre
Verifique a assinatura sempre
Nunca processe um webhook sem validar a assinatura HMAC. Sem isso, qualquer um que conheça sua URL pode forjar eventos.
Dedupe pelo id do evento
Dedupe pelo id do evento
Grave o
id (evt_...) e ignore repetições. Reentregas e reenvios manuais tornam a entrega at-least-once.Use HTTPS
Use HTTPS
URLs de webhook devem usar HTTPS. Endpoints HTTP não são aceitos.
Monitore as entregas
Monitore as entregas
Acompanhe o status em Configurações → Webhooks → Entregas e investigue falhas persistentes antes que esgotem as tentativas.
Solução de problemas
| Sintoma | Causa provável | O que fazer |
|---|---|---|
Você responde 401/erro de assinatura | Secret errado, corpo já parseado, ou secret legado chargefy_whs_ | Use o secret whsec_... correto, receba o corpo bruto e rotacione secrets antigos. |
| Eventos param de chegar | URL inválida, não-HTTPS, firewall, ou endpoint removido | Confira a URL (HTTPS) e o status do endpoint no dashboard. |
| Tudo chega como timeout | Handler lento (> 20s) | Responda 200 na hora e processe em background. |
| Evento recebido mais de uma vez | Reentrega após falha ou reenvio manual | Dedupe pelo id do evento. |
| Falha permanente após ~10 min | Endpoint indisponível além da janela de retry | Resolva o endpoint e use a reentrega manual. |
Próximos passos
Eventos disponíveis
Catálogo completo de eventos e o shape de cada
data.object.API Reference
Recursos públicos, autenticação e convenções da API.

