Webhooks
Bona Fide skickar en HTTPS-POST till din slutpunkt för varje kuverthändelse du prenumererar på. Leveranserna följer
specifikationen Standard Webhooks, så alla dess bibliotek kan verifiera dem.
Registrera en slutpunkt
Section titled “Registrera en slutpunkt”Under Inställningar › Webhooks, eller med en API-nyckel som har
omfånget 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 innehåller secret (whsec_…), bara den här gången. En tom lista i events prenumererar på alla
händelser. URL:en måste vara en offentlig https://-adress. En slutpunkt tillhör ett läge: slutpunkter som skapats
med en testnyckel tar bara emot händelser för kuvert i testläge, och liveslutpunkter bara livehändelser.
PATCH /v1/webhook-endpoints/{id} ändrar URL:en, händelserna eller beskrivningen och pausar eller återupptar
slutpunkten (enabled). POST /v1/webhook-endpoints/{id}/test skickar en webhook.test-ping.
Händelser
Section titled “Händelser”| Typ | När |
|---|---|
envelope.created |
Kuvertet har skapats |
envelope.ready |
Dokumentet är renderat och kuvertet är klart för sina mottagare |
recipient.sent |
Det blev en mottagares tur (och, vid leverans via e-post, hens länk skickades) |
recipient.viewed |
En mottagare öppnade signeringssidan för första gången |
recipient.signed |
En mottagare skickade in (eller kontrasignerades automatiskt) |
recipient.declined |
En mottagare avböjde |
envelope.completed |
Alla mottagare är klara. data.values innehåller variablerna och alla ifyllda värden |
envelope.sealed |
Den förseglade PDF:en kan laddas ned |
envelope.declined |
Kuvertet avslutades eftersom en mottagare avböjde |
envelope.voided |
Du makulerade kuvertet |
envelope.expired |
expiresAt passerades innan kuvertet slutfördes |
signing_link.used |
En besökare startade ett kuvert från en permanent signeringslänk |
acceptance.created är reserverad i schemat och genereras inte än.
Nyttolast
Section titled “Nyttolast”{ "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 är hela kuvertet vid tidpunkten för händelsen. data.values finns bara på
envelope.completed: de variabler du skickade och varje rolls fältvärden per fält-id.
Rubriker och signatur
Section titled “Rubriker och signatur”Varje leverans har:
| Rubrik | Värde |
|---|---|
webhook-id |
Händelsens id (evt_…); detsamma vid varje omförsök. Använd det för deduplicering |
webhook-timestamp |
Unix-sekunder när det här försöket signerades |
webhook-signature |
v1,<base64 HMAC-SHA256>, avgränsade med blanksteg när fler än en hemlighet signerar |
user-agent |
BonaFide-Webhooks/1 |
Signaturen är HMAC-SHA256 över {webhook-id}.{webhook-timestamp}.{raw body}, med den base64-avkodade delen av
hemligheten efter whsec_ som nyckel. Verifiera den råa brödtexten (före JSON-tolkningen), jämför i konstant tid
och avvisa tidsstämplar som avviker mer än fem minuter från din klocka.
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);}Omförsök
Section titled “Omförsök”En leverans lyckas vid valfritt 2xx inom 10 sekunder. Omdirigeringar följs inte. Allt annat görs om med
backoff, efter 5 sekunder, 5 minuter, 30 minuter, 2 timmar, 5 timmar, 10 timmar, 14 timmar, 20 timmar och 24 timmar,
i högst tre dagar efter händelsen. Därefter markeras leveransen som misslyckad.
- Svara snabbt och bearbeta asynkront; deduplicera på
webhook-id. - Leveranser kan komma i fel ordning: kontrollera
data.envelope.statusi stället för att anta en viss ordningsföljd. GET /v1/webhook-endpoints/{id}/deliverieslistar de senaste försöken med statuskoder, fel och tidsåtgång, och varje slutpunkt visar sin andel lyckade leveranser de senaste 24 timmarna (health).POST /v1/events/{id}/redeliverskickar en händelse till en slutpunkt igen, till exempel efter ett avbrott.
Rotera hemligheten
Section titled “Rotera hemligheten”POST /v1/webhook-endpoints/{id}/secret/rotate returnerar en ny hemlighet direkt. Under de följande 24 timmarna
signeras varje leverans med både den nya och den gamla hemligheten (två poster i webhook-signature), så att du
kan driftsätta den nya hemligheten utan att tappa händelser.