Webhooks
Bona Fide sender en HTTPS-POST til dit slutpunkt for hver kuverthændelse, du abonnerer på. Leveringerne følger
specifikationen Standard Webhooks, så alle dens biblioteker kan verificere dem.
Registrering af et slutpunkt
Sektion kaldt “Registrering af et slutpunkt”Under Indstillinger › Webhooks eller med en API-nøgle, der har omfanget
org:manage:
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"] }'Svaret indeholder secret (whsec_…), kun denne ene gang. En tom events-liste abonnerer på alle hændelser.
URL’en skal være en offentlig https://-adresse. Et slutpunkt hører til én tilstand: slutpunkter, der er oprettet
med en testnøgle, modtager kun hændelser for kuverter i testtilstand, og live-slutpunkter kun live-hændelser.
PATCH /v1/webhook-endpoints/{id} ændrer URL’en, hændelserne eller beskrivelsen og sætter slutpunktet på pause
eller genoptager det (enabled). POST /v1/webhook-endpoints/{id}/test sender et webhook.test-ping.
Hændelser
Sektion kaldt “Hændelser”| Type | Hvornår |
|---|---|
envelope.created |
Kuverten er oprettet |
envelope.ready |
Dokumentet er renderet, og kuverten er klar til sine modtagere |
recipient.sent |
Det blev en modtagers tur (og ved levering pr. e-mail blev vedkommendes link sendt) |
recipient.viewed |
En modtager åbnede underskriftssiden for første gang |
recipient.signed |
En modtager indsendte (eller blev medunderskrevet automatisk) |
recipient.declined |
En modtager afviste |
envelope.completed |
Alle modtagere er færdige. data.values indeholder variablerne og alle udfyldte værdier |
envelope.sealed |
Den forseglede PDF er klar til download |
envelope.declined |
Kuverten blev afsluttet, fordi en modtager afviste |
envelope.voided |
Du annullerede kuverten |
envelope.expired |
expiresAt blev passeret før fuldførelsen |
signing_link.used |
En besøgende startede en kuvert fra et permanent underskriftslink |
acceptance.created er reserveret i skemaet og udsendes endnu ikke.
Payload
Sektion kaldt “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 er hele kuverten på tidspunktet for hændelsen. data.values findes kun på
envelope.completed: de variabler, du sendte, og hver rolles feltværdier pr. felt-id.
Headers og signatur
Sektion kaldt “Headers og signatur”Hver levering har:
| Header | Værdi |
|---|---|
webhook-id |
Hændelsens id (evt_…); det samme ved hvert genforsøg. Brug det til deduplikering |
webhook-timestamp |
Unix-sekunder, da dette forsøg blev signeret |
webhook-signature |
v1,<base64 HMAC-SHA256>, adskilt med mellemrum, når mere end én hemmelighed signerer |
user-agent |
BonaFide-Webhooks/1 |
Signaturen er HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body} med den base64-afkodede del af
hemmeligheden efter whsec_ som nøgle. Verificér den rå body (før JSON-parsing), sammenlign i konstant tid, og
afvis tidsstempler, der afviger mere end fem minutter fra dit ur.
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);}Genforsøg
Sektion kaldt “Genforsøg”En levering lykkes ved enhver 2xx inden for 10 sekunder. Omdirigeringer følges ikke. Alt andet forsøges igen med
backoff efter 5 sekunder, 5 minutter, 30 minutter, 2 timer, 5 timer, 10 timer, 14 timer, 20 timer og 24 timer, i
højst tre dage efter hændelsen. Derefter markeres leveringen som mislykket.
- Svar hurtigt, og behandl asynkront; dedupliker på
webhook-id. - Leveringer kan ankomme i forkert rækkefølge: tjek
data.envelope.statusi stedet for at gå ud fra en bestemt rækkefølge. GET /v1/webhook-endpoints/{id}/deliveriesviser de seneste forsøg med statuskoder, fejl og tidsforbrug, og hvert slutpunkt viser sin succesrate for de seneste 24 timer (health).POST /v1/events/{id}/redeliversender en hændelse til et slutpunkt igen, f.eks. efter et nedbrud.
Rotation af hemmeligheden
Sektion kaldt “Rotation af hemmeligheden”POST /v1/webhook-endpoints/{id}/secret/rotate returnerer straks en ny hemmelighed. I de følgende 24 timer signeres
hver levering med både den nye og den gamle hemmelighed (to poster i webhook-signature), så du kan udrulle den
nye hemmelighed uden at miste hændelser.