Webhooks
Bona Fide stuurt een HTTPS-POST naar uw endpoint voor elk envelopevent waarop u zich abonneert. Leveringen volgen
de specificatie Standard Webhooks, dus elke bibliotheek daarvan kan ze verifiëren.
Een endpoint registreren
Section titled “Een endpoint registreren”In Instellingen › Webhooks, of met een API-key die de scope org:manage
heeft:
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"] }'Het antwoord bevat secret (whsec_…), alleen deze ene keer. Een lege events-lijst abonneert op alle events.
De URL moet een openbare https://-URL zijn. Een endpoint hoort bij één modus: endpoints die met een testkey zijn
aangemaakt, ontvangen alleen events van enveloppen in testmodus, live endpoints alleen live events.
PATCH /v1/webhook-endpoints/{id} wijzigt de URL, de events of de beschrijving en pauzeert of hervat het endpoint
(enabled). POST /v1/webhook-endpoints/{id}/test stuurt een webhook.test-ping.
Events
Section titled “Events”| Type | Wanneer |
|---|---|
envelope.created |
De envelop is aangemaakt |
envelope.ready |
Het document is gerenderd en de envelop is klaar voor de ontvangers |
recipient.sent |
Een ontvanger is aan de beurt (en bij bezorging per e-mail is de link verstuurd) |
recipient.viewed |
Een ontvanger heeft de ondertekeningspagina voor het eerst geopend |
recipient.signed |
Een ontvanger heeft ingediend (of is automatisch medeondertekend) |
recipient.declined |
Een ontvanger heeft geweigerd |
envelope.completed |
Alle ontvangers zijn klaar. data.values bevat de variabelen en alle ingevulde waarden |
envelope.sealed |
De verzegelde pdf staat klaar om te downloaden |
envelope.declined |
De envelop is beëindigd omdat een ontvanger heeft geweigerd |
envelope.voided |
U hebt de envelop ingetrokken |
envelope.expired |
expiresAt is verstreken vóór voltooiing |
signing_link.used |
Een bezoeker is een envelop gestart via een permanente ondertekeningslink |
acceptance.created is gereserveerd in het schema en wordt nog niet verstuurd.
Payload
Section titled “Payload”{ "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 is de volledige envelop op het moment van het event. data.values staat
alleen in envelope.completed: de variabelen die u hebt verstuurd en de veldwaarden van elke rol per veld-id.
Headers en handtekening
Section titled “Headers en handtekening”Elke levering bevat:
| Header | Waarde |
|---|---|
webhook-id |
Het event-id (evt_…); bij elke nieuwe poging hetzelfde. Gebruik het om te ontdubbelen |
webhook-timestamp |
Unix-seconden van het moment waarop deze poging is ondertekend |
webhook-signature |
v1,<base64 HMAC-SHA256>, gescheiden door spaties als meer dan één geheim ondertekent |
user-agent |
BonaFide-Webhooks/1 |
De handtekening is een HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body}, met als sleutel het
base64-gedecodeerde deel van het geheim na whsec_. Verifieer de ruwe body (vóór het parsen van de JSON),
vergelijk in constante tijd en weiger tijdstempels die meer dan vijf minuten van uw klok afwijken.
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);});# pip install standardwebhooks flaskimport osfrom flask import Flask, requestfrom standardwebhooks.webhooks import Webhook
wh = Webhook(os.environ["BONA_FIDE_WEBHOOK_SECRET"]) # whsec_…app = Flask(__name__)
@app.post("/hooks/bona-fide")def bona_fide(): try: event = wh.verify(request.get_data(), dict(request.headers)) except Exception: return "", 400 enqueue(event) return "", 204import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret: string, headers: Record<string, string>, rawBody: string) { const id = headers['webhook-id']; const ts = Number(headers['webhook-timestamp']); if (!id || !ts || Math.abs(Date.now() / 1000 - ts) > 300) throw new Error('stale or missing headers'); const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64'); const expected = createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest(); const ok = (headers['webhook-signature'] ?? '').split(' ').some((part) => { const [version, sig] = part.split(','); const given = Buffer.from(sig ?? '', 'base64'); return version === 'v1' && given.length === expected.length && timingSafeEqual(given, expected); }); if (!ok) throw new Error('bad signature'); return JSON.parse(rawBody);}Nieuwe pogingen
Section titled “Nieuwe pogingen”Een levering slaagt bij elke 2xx binnen 10 seconden. Redirects worden niet gevolgd. In alle andere gevallen wordt
de levering met backoff opnieuw geprobeerd, na 5 seconden, 5 minuten, 30 minuten, 2 uur, 5 uur, 10 uur, 14 uur,
20 uur en 24 uur, tot maximaal drie dagen na het event. Daarna wordt de levering als mislukt gemarkeerd.
- Antwoord snel en verwerk asynchroon; ontdubbel op
webhook-id. - Leveringen kunnen in een andere volgorde binnenkomen: controleer
data.envelope.statusin plaats van uit te gaan van een vaste volgorde. GET /v1/webhook-endpoints/{id}/deliveriestoont recente pogingen met statuscodes, fouten en doorlooptijden, en elk endpoint toont het slagingspercentage over 24 uur (health).POST /v1/events/{id}/redeliverstuurt een event opnieuw naar een endpoint, bijvoorbeeld na een storing.
Het geheim roteren
Section titled “Het geheim roteren”POST /v1/webhook-endpoints/{id}/secret/rotate geeft direct een nieuw geheim terug. De volgende 24 uur wordt elke
levering ondertekend met zowel het nieuwe als het oude geheim (twee vermeldingen in webhook-signature), zodat u
het nieuwe geheim kunt uitrollen zonder events te missen.