Aller au contenu

Webhooks

Bona Fide envoie une requête HTTPS POST à votre point de terminaison pour chaque événement d’enveloppe auquel vous êtes abonné. Les livraisons respectent la spécification Standard Webhooks : n’importe laquelle de ses bibliothèques peut donc les vérifier.

Dans Paramètres › Webhooks, ou avec une clé d’API dotée de la portée org:manage :

Fenêtre 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 réponse contient le secret (whsec_…), cette fois seulement. Une liste events vide abonne le point de terminaison à tous les événements. L’URL doit être publique et en https://. Un point de terminaison appartient à un seul mode : les points de terminaison créés avec une clé de test ne reçoivent que les événements des enveloppes en mode test, et ceux de production, que les événements de production.

PATCH /v1/webhook-endpoints/{id} modifie l’URL, les événements ou la description, et suspend ou réactive le point de terminaison (enabled). POST /v1/webhook-endpoints/{id}/test envoie un ping webhook.test.

Type Quand
envelope.created L’enveloppe a été créée
envelope.ready Le document est généré et l’enveloppe est prête pour ses destinataires
recipient.sent C’est devenu le tour d’un destinataire (et, avec la livraison par courriel, son lien a été envoyé)
recipient.viewed Un destinataire a ouvert la page de signature pour la première fois
recipient.signed Un destinataire a soumis (ou a été contresigné automatiquement)
recipient.declined Un destinataire a refusé
envelope.completed Tous les destinataires ont terminé. data.values contient les variables et toutes les valeurs saisies
envelope.sealed Le PDF scellé est prêt à être téléchargé
envelope.declined L’enveloppe a pris fin parce qu’un destinataire a refusé
envelope.voided Vous avez annulé l’enveloppe
envelope.expired La date expiresAt est passée avant la finalisation
signing_link.used Un visiteur a lancé une enveloppe à partir d’un lien de signature permanent

acceptance.created est réservé dans le schéma, mais n’est pas encore émis.

{
"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 est l’enveloppe complète au moment de l’événement. data.values n’est présent que dans envelope.completed : les variables que vous avez envoyées, et les valeurs des champs de chaque rôle, par identifiant de champ.

Chaque livraison comporte :

En-tête Valeur
webhook-id L’identifiant de l’événement (evt_…); le même à chaque nouvelle tentative. Utilisez-le pour dédoublonner
webhook-timestamp Le moment, en secondes Unix, où cette tentative a été signée
webhook-signature v1,<base64 HMAC-SHA256>, séparés par des espaces quand plusieurs secrets signent
user-agent BonaFide-Webhooks/1

La signature est un HMAC-SHA256 calculé sur {webhook-id}.{webhook-timestamp}.{raw body}, avec pour clé la partie du secret qui suit whsec_, décodée en base64. Vérifiez le corps brut (avant l’analyse JSON), comparez en temps constant et rejetez les horodatages qui s’écartent de plus de cinq minutes de votre horloge.

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

Une livraison réussit sur toute réponse 2xx reçue dans un délai de 10 secondes. Les redirections ne sont pas suivies. Dans tous les autres cas, la livraison est relancée avec un délai croissant : après 5 secondes, 5 minutes, 30 minutes, 2 heures, 5 heures, 10 heures, 14 heures, 20 heures et 24 heures, pendant au plus trois jours après l’événement. La livraison est ensuite marquée comme ayant échoué.

  • Répondez rapidement et traitez de façon asynchrone; dédoublonnez selon webhook-id.
  • Les livraisons peuvent arriver dans le désordre : vérifiez data.envelope.status plutôt que de présumer une séquence.
  • GET /v1/webhook-endpoints/{id}/deliveries liste les tentatives récentes avec les codes d’état, les erreurs et les durées, et chaque point de terminaison affiche son taux de réussite sur 24 heures (health).
  • POST /v1/events/{id}/redeliver envoie de nouveau un événement à un point de terminaison, par exemple après une panne.

POST /v1/webhook-endpoints/{id}/secret/rotate renvoie immédiatement un nouveau secret. Pendant les 24 heures suivantes, chaque livraison est signée avec à la fois le nouveau et l’ancien secret (deux entrées dans webhook-signature) : vous pouvez ainsi déployer le nouveau secret sans perdre d’événements.