Receber webhooks
Um webhook é o AssinaJá a bater à porta do seu sistema quando alguma coisa
acontece a um documento, em vez de o seu sistema andar a perguntar. É um pedido
POST que a plataforma faz a um endereço seu.
Isto é matéria para quem integra o AssinaJá com outro programa. Se só quer ver o que se passou com um documento, use o histórico do documento.
O endereço que a plataforma aceita
Seção intitulada “O endereço que a plataforma aceita”O endereço não é livre. A plataforma valida-o antes de o guardar, para não poder ser usada para bater em máquinas que não são suas:
- HTTPS, sempre.
- Porta 443 ou 8443, e mais nenhuma.
- Um anfitrião público: nem
localhost, nem*.localhost, nem*.internal, nemmetadata.google.internal. - Um endereço público. Se o nome resolver para um endereço privado ou
reservado —
10.x,172.16–31.x,192.168.x,127.x,169.254.x,100.64.x, multicast, ou os equivalentes em IPv6 — é recusado. Todos os endereços a que o nome resolve são verificados, não só o primeiro.
A recusa aparece na sua língua:
| Recusa | O que fazer |
|---|---|
| “Indique o endereço do webhook.” | o campo está vazio |
| “O endereço não é um URL válido.” | falta o esquema, ou há um erro de escrita |
| “O endereço tem de usar HTTPS.” | troque http:// por https:// |
| “A porta tem de ser 443 ou 8443.” | tire a porta do endereço, ou use uma destas |
| “O anfitrião é reservado (localhost, .internal, metadados de cloud) e não pode receber webhooks.” | use um nome alcançável da Internet |
| “O endereço IP não é público.” | escreveu um IP privado directamente |
| “O nome do anfitrião não resolve para nenhum endereço.” | confirme o DNS |
| “Não foi possível resolver o nome do anfitrião.” | o DNS falhou; tente outra vez |
| “O anfitrião resolve para um endereço que não é público.” | o nome aponta para dentro da sua rede |
Ao escolher os eventos, “Um dos eventos escolhidos não é reconhecido.” quer dizer que a lista enviada tem um nome que a plataforma não conhece — confirme-o na tabela acima. E “Não há endereço de webhook configurado — indique um ou guarde a subscrição primeiro.” aparece quando se tenta subscrever eventos antes de haver endereço.
Os eventos
Seção intitulada “Os eventos”| Evento | Quando é enviado |
|---|---|
document.completed |
Todos assinaram e o documento final está pronto |
document.rejected |
Um signatário recusou |
document.canceled |
O dono cancelou o documento |
document.expired |
O prazo passou sem estar concluído |
document.pdf_application_failed |
Não foi possível aplicar a assinatura ao PDF |
recipient.signed |
Um signatário assinou — uma vez por signatário |
document.sent |
O documento foi publicado e os convites saíram |
Os dois últimos são novos e não são finais: o documento continua a andar depois deles. Não trate nenhum evento como “está terminado” — veja o estado do documento que vem no corpo.
O document.pdf_application_failed é o que não se deve ignorar: quer dizer que alguém assinou mas a
assinatura não chegou a entrar no ficheiro. No AssinaJá isso aparece como
Problema técnico no detalhe do documento, com Tentar de novo — ver
Acompanhar e gerir documentos.
O cabeçalho Idempotency-Key serve para o seu lado descartar repetições, mas
deixou de ter uma forma só. Depende de para onde vai o evento e de qual é:
| Destino | Evento | Chave |
|---|---|---|
Endereço do documento (webhookUrl) |
qualquer, menos recipient.signed |
{documentPublicId}:{event} |
| Endereço do documento | recipient.signed |
{documentPublicId}:{event}:{recipientId} |
| Endereço da organização | qualquer, menos recipient.signed |
{documentPublicId}:{event}:org |
| Endereço da organização | recipient.signed |
{documentPublicId}:{event}:{recipientId}:org |
Guarde e compare a chave inteira, como um texto opaco. Não a parta em pedaços.
Se tem uma restrição de unicidade em (documento, evento) na sua base de dados,
ela passa a recusar entregas legítimas: o recipient.signed chega uma vez por
signatário, e um documento com endereço próprio numa organização que também tem
endereço configurado produz duas entregas do mesmo evento, com chaves
diferentes.
O segredo do webhook
Seção intitulada “O segredo do webhook”O segredo é o que lhe permite ter a certeza de que o pedido veio mesmo do AssinaJá. Está em Definições → Desenvolvedores, no separador Webhooks, sub-separador Segredo. Só o Criador da organização o vê.
Segredo do webhook:
O segredo é utilizado para verificar a assinatura dos eventos enviados para o seu webhook.
Use Gerar segredo da primeira vez, e Copiar para o guardar. Não deixe essa página sem o ter guardado:
Guarde este valor agora — não volta a ser mostrado.
Trocar o segredo
Seção intitulada “Trocar o segredo”Rodar gera um segredo novo sem interromper as entregas:
Tem a certeza que pretende rodar o segredo? O segredo anterior será válido por 24 horas.
Durante essas 24 horas — “Segredo anterior válido mais 24 h.” — os pedidos vão assinados com os dois segredos, separados por vírgula no mesmo cabeçalho. É por isso que a verificação tem de aceitar uma lista, e não um valor único. Passado esse prazo, só o novo é usado.
Verificar a assinatura
Seção intitulada “Verificar a assinatura”Cada pedido traz estes cabeçalhos:
| Cabeçalho | O que é |
|---|---|
X-AssinaJa-Signature |
A assinatura, v1=<hex>, ou várias separadas por vírgula durante uma rotação |
X-AssinaJa-Event |
O nome do evento |
X-AssinaJa-Delivery |
Um identificador novo em cada tentativa — não serve para descartar repetições |
X-AssinaJa-Timestamp |
A hora, em segundos Unix |
Idempotency-Key |
Ver o quadro acima — quatro formas possíveis |
A assinatura é um HMAC-SHA256 sobre a hora e o corpo do pedido juntos por um
ponto — {timestamp}.{corpo} — em hexadecimal minúsculo e prefixado por v1=.
A chave não é o segredo tal como o copiou. O segredo vem codificado em base64url; a chave do HMAC são os bytes que resultam de o descodificar. Passar o texto directamente à função de HMAC produz uma assinatura que nunca bate certo — é a causa mais comum de a verificação falhar.
const crypto = require('crypto');
function verifyWebhookSignature(secret, timestamp, body, signatureHeader) { const message = `${timestamp}.${body}`; // o segredo é base64url — a chave são os bytes descodificados, não o texto const secretBytes = Buffer.from(secret, 'base64url'); const expected = 'v1=' + crypto .createHmac('sha256', secretBytes) .update(message) .digest('hex');
// durante uma rotação vêm as duas assinaturas const signatures = signatureHeader.split(',').map(s => s.trim()); return signatures.some(sig => sig === expected);}Use o corpo tal como chegou, em bruto. Se o passar por um analisador de JSON e voltar a serializá-lo, os bytes mudam e a assinatura deixa de bater certo.
O que a plataforma faz com a sua resposta
Seção intitulada “O que a plataforma faz com a sua resposta”| O que responde | O que acontece |
|---|---|
2xx |
Entregue. Não há nova tentativa. O corpo da resposta tem de caber em 4 KB. |
3xx |
Volta a tentar — os reencaminhamentos não são seguidos. |
4xx |
Volta a tentar, tal como um 5xx. |
5xx |
Volta a tentar. |
| Sem resposta em 10 segundos | Volta a tentar. |
Só uma resposta 2xx conta como entregue. Não existe nenhum código que faça
a plataforma desistir de propósito: se quer que uma entrega pare, responda 2xx e
deite o conteúdo fora do seu lado.
As tentativas são espaçadas de 1 min, 2 min, 5 min, 15 min, 30 min, 60 min e 60 min. São oito ao todo, e a oitava é uma entrega a sério — só quando ela falha é que a entrega fica marcada como falhada.
Ver se chegou
Seção intitulada “Ver se chegou”O detalhe de cada documento mostra a última entrega desse documento, seja de que evento for, no cartão Integração — com Webhook (o nome do evento), Estado, Tentativas, Entregue em e, se falhou, o Erro. É o sítio mais rápido para distinguir “o AssinaJá não enviou” de “o meu servidor não aceitou”. Não há botão de reenvio neste cartão.
Para ver o histórico completo da organização e reenviar entregas falhadas, vá a Definições → Desenvolvedores → Webhooks, ao sub-separador Entregas:
Cada evento enviado, com a resposta do seu endereço. As entregas falhadas podem ser reenviadas.
Só o Criador da organização tem acesso a esse ecrã.
Quando o reenvio não pega, o ecrã diz porquê:
Essa entrega já não existe.
Essa entrega já foi recebida — só as falhadas se reenviam.
Não é possível reenviar esta entrega neste momento.