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, de modo que cualquiera de sus bibliotecas puede verificarlas.

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

Ventana de 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"]
}'

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 empezar por https://. Cada endpoint pertenece a un único 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 ha creado el sobre
envelope.ready El documento se ha generado y el sobre está listo para sus destinatarios
recipient.sent Le ha llegado el turno a un destinatario (y, con la entrega por correo, se le ha enviado su enlace)
recipient.viewed Un destinatario ha abierto la página de firma por primera vez
recipient.signed Un destinatario ha enviado su firma (o se ha contrafirmado automáticamente)
recipient.declined Un destinatario ha rechazado la firma
envelope.completed Todos los destinatarios han terminado. data.values contiene las variables y todos los valores introducidos
envelope.sealed El PDF sellado está listo para descargarse
envelope.declined El sobre ha terminado porque un destinatario ha rechazado la firma
envelope.voided Usted ha anulado el sobre
envelope.expired expiresAt ha pasado antes de completarse
signing_link.used Un visitante ha iniciado un sobre desde un enlace de firma permanente

acceptance.created está reservado en el esquema y aún 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 ha enviado y los valores de los campos de cada rol, por ID de campo.

Cada entrega incluye:

Cabecera Valor
webhook-id El ID del evento (evt_…); es el mismo en todos los reintentos. Utilícelo para eliminar duplicados
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}, cuya clave es la parte del secreto que sigue a whsec_, decodificada en base64. Verifique el cuerpo en bruto (antes de analizar el JSON), compare en tiempo constante y rechace las marcas de tiempo que se alejen 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 en un plazo de 10 segundos. Las redirecciones no se siguen. Todo lo demás se reintenta con esperas crecientes, a los 5 segundos, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas, 14 horas, 20 horas y 24 horas, durante tres días como máximo desde el evento. Después, la entrega se marca como fallida.

  • Responda rápidamente y procese de forma asíncrona; elimine duplicados por webhook-id.
  • Las entregas pueden llegar desordenadas: compruebe data.envelope.status en lugar de dar por hecho un orden.
  • GET /v1/webhook-endpoints/{id}/deliveries muestra los intentos recientes con códigos de estado, errores y tiempos, y cada endpoint indica 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, tras una caída 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 desplegar el secreto nuevo sin perder eventos.