POST assinado para essa URL e insiste
até receber um 2xx. É a volta do webhook de entrada: lá a
plataforma de venda avisa o club; aqui o club avisa você.
O formato segue o Standard Webhooks, então as bibliotecas
oficiais do padrão conferem a assinatura sem código nosso.
A requisição
O envelope
Todo evento tem a mesma casca:Catálogo
event_types é uma lista explícita, sem coringa: um endpoint recebe só os tipos que
escolheu, e um tipo novo que entrar no catálogo nunca começa a sair para quem não o pediu.
O documento do aluno (CPF) não sai em evento nenhum.
Verificar a assinatura
A assinatura é um HMAC-SHA256 sobre{webhook-id}.{webhook-timestamp}.{corpo}. A chave é o
segredo sem o prefixo whsec_, decodificado de base64; o resultado vai em base64 depois de
v1,.
Três cuidados que valem para qualquer linguagem:
- Assine o corpo cru, os bytes exatos que chegaram, antes de qualquer parse. Um JSON lido e serializado de novo muda espaço, ordem ou escape de acento, e a assinatura deixa de conferir.
- Aceite qualquer uma das assinaturas do cabeçalho: na troca de segredo vêm duas.
- Recuse o que chega velho: timestamp com mais de 5 minutos de diferença do seu relógio. Como o timestamp entra na assinatura, uma requisição capturada não pode ser repetida depois.
O segredo
O segredo (whsec_…) aparece uma única vez: na resposta de
Cadastrar endpoint e na de
Trocar segredo. Perdeu, troque.
Na troca, o segredo anterior continua assinando por 24 horas: nesse intervalo cada entrega
leva as duas assinaturas, e o seu lado aceita a que conferir. Atualize o segredo do seu lado
dentro desse prazo e nenhuma entrega fica sem conferir no meio do caminho.
Responder
Responda2xx rápido e processe depois. Cada tentativa tem 15 segundos de prazo;
grave o evento numa fila sua e responda, em vez de fazer o trabalho dentro da requisição.
- Qualquer resposta fora de
2xxconta como falha, inclusive4xx: um401depois de trocar o segredo ou um404de um deploy pela metade se consertam do seu lado, e a entrega volta a tentar. - Redirecionamento (
3xx) não é seguido. Cadastre a URL final. Retry-Afterem429e503é respeitado, até 10 horas: ele alonga a espera até a próxima tentativa, nunca a encurta.
Novas tentativas
Uma entrega tem 8 tentativas no total. Depois de cada falha, a próxima espera:
Cada espera varia ±10%, para mil entregas que falharam juntas não voltarem juntas. São cerca de
27 horas entre a primeira e a última tentativa; esgotadas as oito, a entrega fica
failed.
Reenviar entrega tenta agora, com o mesmo
corpo e o mesmo webhook-id, e o efeito depende do estado da entrega:
- Fechada (
succeeded,failedoucanceled): ganha uma tentativa manual. Falhando, volta afailed, sem voltar para a agenda. pending: a próxima tentativa só é antecipada, e a entrega continua na agenda.
Entrega pelo menos uma vez, e sem ordem
A mesma mensagem pode chegar mais de uma vez — a sua resposta se perdeu na rede, alguém reenviou pelo painel. Deduplique pelowebhook-id: guarde os ids já processados e ignore o
repetido.
Não há garantia de ordem. Uma falha numa tentativa faz o evento seguinte chegar antes. Não
decida pelo tipo da última mensagem que chegou: use o occurred_at e o estado do recurso que
vem no data. O corpo é montado segundos depois do fato e traz o recurso como está naquele
momento — num access.revoked, data.enrollment.status já é revoked.
O disjuntor
Um endpoint que só falha não deve receber rajadas:- A partir de 5 falhas seguidas, o endpoint passa a receber uma tentativa por vez, com
intervalos que começam em 1 minuto e dobram até 1 hora (
hold_until). - Qualquer
2xxzera o disjuntor. - Cinco dias sem nenhuma entrega aceita desligam o endpoint (
status: disabled,disabled_reason: failing). A fila dele é cancelada e quem administra o club recebe um e-mail.
status: enabled), o que zera o disjuntor, e chame
Recuperar entregas: o que falhou, ou foi cancelado
porque o endpoint estava desligado, desde a data informada — até 30 dias atrás — volta para a
fila, com uma tentativa cada. O que foi cancelado pela remoção de um aluno ou por expirar não
volta: o corpo dele não existe mais.
Estados do endpoint
Pausar é para uma manutenção do seu lado, em que nada pode se perder. Desligar é para parar de
receber.
O endereço
- Precisa ser
httpse resolver para a internet pública. Rede privada,localhoste IPs reservados são recusados no cadastro — e de novo a cada conexão, contra o IP que o DNS devolveu naquela hora. - Não há IP fixo de saída: não dá para liberar a entrega por lista de IPs. A assinatura é o que prova que a mensagem veio do club.
- Até 20 endpoints por club.
- Cadastrar e gerir endpoints exige permissão de administração do club: o histórico de entregas leva nome e e-mail de alunos.
Testar
Enviar teste cria uma entregawebhook.test que passa
pelo mesmo caminho dos eventos reais: assinatura, conferência do endereço, novas tentativas.
O teste que passa prova o que vai acontecer com os eventos de verdade. Só funciona com o endpoint
enabled, e não espera o disjuntor: é o jeito de conferir, na hora, que o seu lado voltou.
Retenção
Depois que o corpo é apagado, a entrega continua no histórico com
payload_purged, e não há mais
o que reenviar.
O prazo do corpo não tem exceção. Uma entrega que fica pendente mais de 30 dias — o caso é o
endpoint pausado e esquecido — é cancelada com cancel_reason: expired, para o corpo sair no
prazo como todos os outros.
Remoção de aluno
Quando um aluno é removido do club (LGPD):- sai
student.removed, só com o id — o sinal para apagar a pessoa do seu sistema também; - os corpos das mensagens anteriores que falavam dela são apagados;
- o que ainda não tinha saído é cancelado (
cancel_reason: student_removed); - eventos daquele aluno gravados depois da remoção não saem.