Registrar um endpoint
https://. As entregas carregam fatos sobre os seus próprios clientes e
uma assinatura que prova quem enviou, e nenhum dos dois sobrevive a ser lido no
fio. Um redirect é recusado em vez de seguido — registre o endereço que de fato
responde.
Um endpoint pertence a um ambiente e nunca sai dele: um endpoint de teste
ouve tráfego de teste, um de produção ouve tráfego de produção. Fatos sobre a
própria organização (organization.created, member.invited) não carregam
ambiente e vão para os endpoints live — uma organização tem uma existência
real só, e o ambiente de teste é onde o plano de clientes é ensaiado, não onde a
organização está.
subscribed_events vazio significa todos. Um tipo adicionado ao catálogo
depois passa a chegar sem você mexer em nada, o que costuma ser o que se quer no
primeiro endpoint e raramente é o que se quer no terceiro.
O payload
data carrega ids, não fotografias — releia o recurso para saber o estado
atual. Isso mantém nomes e endereços fora de toda fila, log e buffer de retry
entre nós e você, e significa que um handler atrasado lê o que é verdade agora em
vez do que era verdade quando o evento foi escrito.
A entrega é ao menos uma vez, e fora de ordem
Isso é o contrato, não uma ressalva. A mesma entrega pode chegar duas vezes e a ordem de chegada não é a ordem dos fatos. Dois campos existem para tornar isso tratável, e usá-los não é opcional:idé por onde você deduplica. Ele é estável em todo retry e em um reenvio manual — um reenvio é o mesmo fato chegando de novo, e um handler que já processou precisa poder dizer isso. Anote o id antes de agir sobre o evento; ignore o que já viu.sequenceé por onde você ordena. É um número global monotônico atribuído no commit: de dois eventos sobre o mesmo contato, o maior é o fato mais recente. Compare-o. Não compare horários de chegada, e não assuma quecontact.identifiedchega antes decontact.signed_in.
UserKit-Delivery-Id
nomeia este conjunto de tentativas. Um retry mantém o mesmo, um reenvio ganha
outro. É o que se cita numa conversa de suporte; UserKit-Event-Id é o que o seu
código usa para decidir.
Verificando a assinatura
Toda requisição carrega:v1 é HMAC-SHA256(segredo, "<t>.<corpo cru>"), em hex. Assine os bytes
crus que você recebeu — fazer parse e serializar de novo muda os bytes.
v1 como uma etiqueta em vez de uma
suposição: leia os pares que você conhece e ignore o resto, para que um algoritmo
futuro possa ser adicionado ao lado deste em vez de no lugar dele.
O segredo pode ser exibido de novo
Diferente de uma API key, você pode lê-lo outra vez emGET /v1/organization/webhooks/{id}/secret.
Isso é uma consequência, não uma conveniência. Uma API key só é comparada,
então guardar o hash basta; um segredo de webhook precisa assinar cada
entrega, então a linha guarda o segredo de qualquer jeito. Uma vez que isso é
verdade, mostrá-lo uma única vez só impediria você de ler o que o banco contém às
claras — ao custo da única recuperação que não é “rotacionar e quebrar todos os
verificadores em produção de uma vez”.
A rotação é o controle de verdade, e ela não tem janela de tolerância: o novo
segredo vale a partir da próxima entrega. Dois segredos vivos significariam um
verificador que aceita qualquer um dos dois, que é exatamente a propriedade que a
rotação existe para remover. Publique o segredo novo primeiro, rotacione depois.
Retentativas, e quando paramos
O seu endpoint tem dez segundos para responder. Qualquer2xx é sucesso;
confirme primeiro e faça o trabalho depois.
Uma tentativa que falha é retentada doze vezes, dobrando a partir de 30
segundos e com teto de seis horas:
Desligamento automático
Cinco entregas seguidas esgotando todas as tentativas desligam o endpoint. Isso leva pelo menos as quatorze horas que a primeira delas passou retentando, sobre cinco fatos diferentes, que é o que “sustentado” precisa significar antes de pararmos de chamar o seu servidor. Um único sucesso zera a sequência. Um endpoint desligado não recebe nada novo na fila — e os owners da sua organização recebem um e-mail, porque uma integração que fica quieta sem avisar é descoberta pela ausência. Nada se perde: as entregas estão no histórico, e reenviar uma é um clique assim que o endereço voltar. Religar zera a sequência, então a primeira falha depois do conserto não desliga tudo de novo.O histórico, o reenvio e o disparo de teste
GET /v1/organization/webhooks/{id}/deliveries são as últimas cem entregas, mais
recentes primeiro, com as tentativas gastas, o status que voltou e quanto tempo
levou. Entregas concluídas ficam 30 dias; uma pendente nunca é removida.
POST …/deliveries/{deliveryId}/replay envia o corpo guardado de novo, byte a
byte, com um novo id de entrega e o mesmo id de evento.
POST …/test enfileira uma entrega com o tipo webhook.test — deliberadamente
ausente do catálogo, para que um handler que reage a ele esteja reagindo a um
botão que alguém apertou, não a um fato.
Os dois respondem 202: a linha é durável, a tentativa ainda não aconteceu, e o
resultado aparece no histórico.
O catálogo publicado
Este é o contrato contra o qual você constrói, e é a mesma lista que o fan-out da entrega lê — um tipo ausente daqui é um tipo que não vai chegar.GET /v1/organization/webhooks/events devolve o catálogo em tempo de execução.
Um fato é publicado internamente e nunca entregue:
webhook.endpoint_disabled. Ele seria endereçado ao endpoint que acabou de parar
de responder, o que é uma mensagem para ninguém — o e-mail para os owners é o que
carrega esse recado.
Permissão
Tudo acima exigewebhooks:manage, concedida a owner e admin por padrão. Não
há divisão entre leitura e escrita, porque a tela mostra o segredo de assinatura:
ler um endpoint é possuir a credencial dele.