Pular para o conteúdo

Webhooks

A Bona Fide envia um POST HTTPS para o seu endpoint por cada evento de envelope que subscrever. As entregas seguem a especificação Standard Webhooks, pelo que qualquer uma das respetivas bibliotecas as pode verificar.

Em Definições › Webhooks ou com uma chave de API que tenha o âmbito org:manage:

Janela do terminal
curl https://api.bonafidesign.com/v1/webhook-endpoints \
-H "Authorization: Bearer $BONA_FIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/hooks/bona-fide",
"description": "Production",
"events": ["envelope.completed", "envelope.sealed", "envelope.declined"]
}'

A resposta inclui o secret (whsec_…), apenas desta vez. Uma lista events vazia subscreve todos os eventos. O URL tem de ser https:// público. Um endpoint pertence a um único modo: os endpoints criados com uma chave de teste só recebem eventos de envelopes do modo de teste, e os de produção só recebem eventos de produção.

PATCH /v1/webhook-endpoints/{id} altera o URL, os eventos ou a descrição e coloca o endpoint em pausa ou retoma-o (enabled). POST /v1/webhook-endpoints/{id}/test envia um ping webhook.test.

Tipo Quando
envelope.created O envelope foi criado
envelope.ready O documento foi gerado e o envelope está pronto para os destinatários
recipient.sent Chegou a vez de um destinatário (e, com entrega por e-mail, a respetiva ligação foi enviada)
recipient.viewed Um destinatário abriu a página de assinatura pela primeira vez
recipient.signed Um destinatário submeteu (ou foi contra-assinado automaticamente)
recipient.declined Um destinatário recusou
envelope.completed Todos os destinatários terminaram. data.values contém as variáveis e todos os valores introduzidos
envelope.sealed O PDF selado está pronto para transferência
envelope.declined O envelope terminou porque um destinatário recusou
envelope.voided Anulou o envelope
envelope.expired O expiresAt passou antes da conclusão
signing_link.used Um visitante iniciou um envelope a partir de uma ligação de assinatura permanente

acceptance.created está reservado no esquema e ainda não é emitido.

{
"id": "evt_9sKq2Lm4Tz",
"type": "envelope.completed",
"createdAt": "2026-10-13T14:09:52.004Z",
"testMode": false,
"data": {
"envelope": {
"id": "env_8Gm2kQ4rT1",
"status": "completed",
"seal": "pending",
"recipients": [{ "id": "rcp_5Jw0pLs9Xe", "role": "client", "status": "completed", "…": "…" }],
"externalId": "user_8841",
"metadata": {},
"…": "…"
},
"values": {
"variables": { "client": { "name": "Acme Robotics, Inc." }, "effective_date": "2026-10-13" },
"recipients": {
"client": { "client_title": "CEO", "start_date": "2026-11-01" }
}
}
}
}

data.envelope é o envelope completo no momento do evento. data.values só existe em envelope.completed: as variáveis que enviou e os valores dos campos de cada função, por id de campo.

Cada entrega inclui:

Cabeçalho Valor
webhook-id O id do evento (evt_…); é o mesmo em todas as repetições. Utilize-o para eliminar duplicados
webhook-timestamp Segundos Unix em que esta tentativa foi assinada
webhook-signature v1,<base64 HMAC-SHA256>, separados por espaços quando assina mais do que um segredo
user-agent BonaFide-Webhooks/1

A assinatura é um HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{raw body}, com a chave formada pela parte do segredo a seguir a whsec_, descodificada de base64. Verifique o corpo em bruto (antes da análise do JSON), compare em tempo constante e rejeite marcas temporais que difiram mais de cinco minutos do seu relógio.

import express from 'express';
import { Webhook } from 'standardwebhooks'; // npm i standardwebhooks
const wh = new Webhook(process.env.BONA_FIDE_WEBHOOK_SECRET!); // whsec_…
const app = express();
app.post('/hooks/bona-fide', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = wh.verify(req.body, req.headers as Record<string, string>); // throws if forged or too old
} catch {
return res.sendStatus(400);
}
queue.add(event); // do the work asynchronously and answer quickly
res.sendStatus(204);
});

Uma entrega é bem-sucedida com qualquer 2xx no prazo de 10 segundos. Os redirecionamentos não são seguidos. Qualquer outra resposta dá origem a repetições com backoff, ao fim de 5 segundos, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas, 14 horas, 20 horas e 24 horas, durante no máximo três dias após o evento. Depois disso, a entrega é marcada como falhada.

  • Responda depressa e processe de forma assíncrona; elimine duplicados com base no webhook-id.
  • As entregas podem chegar fora de ordem: verifique data.envelope.status em vez de presumir uma sequência.
  • GET /v1/webhook-endpoints/{id}/deliveries lista as tentativas recentes com códigos de estado, erros e tempos, e cada endpoint mostra a sua taxa de sucesso nas últimas 24 horas (health).
  • POST /v1/events/{id}/redeliver volta a enviar um evento para um endpoint, por exemplo após uma indisponibilidade.

POST /v1/webhook-endpoints/{id}/secret/rotate devolve imediatamente um novo segredo. Durante as 24 horas seguintes, todas as entregas são assinadas com ambos os segredos, o novo e o antigo (duas entradas em webhook-signature), para que possa implementar o novo segredo sem perder eventos.