Ir al contenido

Webhooks

Bona Fide envía un POST HTTPS a su endpoint por cada evento de sobre al que se suscriba. Las entregas siguen la especificación Standard Webhooks, así que cualquiera de sus bibliotecas puede verificarlas.

En Configuración › Webhooks, o con una clave de API que tenga el alcance org:manage:

Terminal window
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"]
}'

La respuesta incluye secret (whsec_…), solo esta vez. Una lista events vacía suscribe el endpoint a todos los eventos. La URL debe ser pública y usar https://. Un endpoint pertenece a un solo modo: los endpoints creados con una clave de prueba solo reciben eventos de sobres en modo de prueba, y los de producción, solo eventos de producción.

PATCH /v1/webhook-endpoints/{id} cambia la URL, los eventos o la descripción, y pausa o reanuda el endpoint (enabled). POST /v1/webhook-endpoints/{id}/test envía un ping webhook.test.

Tipo Cuándo
envelope.created Se creó el sobre
envelope.ready El documento está generado y el sobre está listo para sus destinatarios
recipient.sent Llegó el turno de un destinatario (y, con la entrega por correo, se envió su enlace)
recipient.viewed Un destinatario abrió la página de firma por primera vez
recipient.signed Un destinatario envió su firma (o se contrafirmó automáticamente)
recipient.declined Un destinatario rechazó la firma
envelope.completed Todos los destinatarios terminaron. data.values contiene las variables y cada valor ingresado
envelope.sealed El PDF sellado está listo para descargar
envelope.declined El sobre finalizó porque un destinatario rechazó la firma
envelope.voided Usted anuló el sobre
envelope.expired expiresAt pasó antes de completarse
signing_link.used Un visitante inició un sobre desde un enlace de firma permanente

acceptance.created está reservado en el esquema y todavía no se emite.

{
"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 es el sobre completo en el momento del evento. data.values solo aparece en envelope.completed: las variables que usted envió y los valores de los campos de cada rol por ID de campo.

Cada entrega incluye:

Encabezado Valor
webhook-id El ID del evento (evt_…); es el mismo en cada reintento. Úselo para deduplicar
webhook-timestamp Segundos Unix del momento en que se firmó este intento
webhook-signature v1,<base64 HMAC-SHA256>, separados por espacios cuando firma más de un secreto
user-agent BonaFide-Webhooks/1

La firma es un HMAC-SHA256 sobre {webhook-id}.{webhook-timestamp}.{raw body}, con la parte del secreto que sigue a whsec_, decodificada en base64, como clave. Verifique el cuerpo sin procesar (antes de analizar el JSON), compare en tiempo constante y rechace las marcas de tiempo que difieran en más de cinco minutos de su reloj.

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);
});

Una entrega se considera correcta con cualquier 2xx dentro de los 10 segundos. No se siguen las redirecciones. Todo lo demás se reintenta con espera progresiva, después de 5 segundos, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas, 14 horas, 20 horas y 24 horas, durante un máximo de tres días después del evento. Luego, la entrega se marca como fallida.

  • Responda rápido y procese de forma asíncrona; deduplique por webhook-id.
  • Las entregas pueden llegar desordenadas: compruebe data.envelope.status en lugar de suponer una secuencia.
  • GET /v1/webhook-endpoints/{id}/deliveries enumera los intentos recientes con códigos de estado, errores y tiempos, y cada endpoint muestra su tasa de éxito de las últimas 24 horas (health).
  • POST /v1/events/{id}/redeliver vuelve a enviar un evento a un endpoint, por ejemplo, después de una interrupción del servicio.

POST /v1/webhook-endpoints/{id}/secret/rotate devuelve un secreto nuevo de inmediato. Durante las 24 horas siguientes, cada entrega se firma con ambos secretos, el nuevo y el anterior (dos entradas en webhook-signature), para que pueda implementar el secreto nuevo sin perder eventos.