Webhook
Bona Fide mengirim HTTPS POST ke endpoint Anda untuk setiap event amplop yang Anda langgani. Pengiriman mengikuti
spesifikasi Standard Webhooks, sehingga library mana pun dari spesifikasi itu
dapat memverifikasinya.
Mendaftarkan endpoint
Section titled “Mendaftarkan endpoint”Di Pengaturan › Webhook, atau dengan kunci API yang memiliki scope
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"] }'Respons menyertakan secret (whsec_…), hanya kali ini saja. Daftar events yang kosong berlangganan ke
semua event. URL harus berupa https:// publik. Sebuah endpoint termasuk dalam satu mode: endpoint yang dibuat
dengan kunci uji hanya menerima event dari amplop mode uji, dan endpoint live hanya menerima event live.
PATCH /v1/webhook-endpoints/{id} mengubah URL, event, atau deskripsi, serta menjeda atau melanjutkan endpoint
(enabled). POST /v1/webhook-endpoints/{id}/test mengirim ping webhook.test.
| Jenis | Kapan |
|---|---|
envelope.created |
Amplop telah dibuat |
envelope.ready |
Dokumen telah dirender dan amplop siap untuk penerimanya |
recipient.sent |
Giliran seorang penerima telah tiba (dan, dengan pengiriman email, tautannya telah dikirim) |
recipient.viewed |
Seorang penerima membuka halaman penandatanganan untuk pertama kali |
recipient.signed |
Seorang penerima telah mengirimkan (atau ditandatangani pendamping secara otomatis) |
recipient.declined |
Seorang penerima menolak |
envelope.completed |
Semua penerima selesai. data.values berisi variabel dan setiap nilai yang diisi |
envelope.sealed |
PDF yang disegel siap diunduh |
envelope.declined |
Amplop berakhir karena seorang penerima menolak |
envelope.voided |
Anda membatalkan amplop |
envelope.expired |
expiresAt terlewati sebelum selesai |
signing_link.used |
Seorang pengunjung memulai amplop dari tautan penandatanganan yang selalu aktif |
acceptance.created sudah dicadangkan di skema dan belum dipancarkan.
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 adalah amplop lengkap pada saat event terjadi. data.values hanya ada pada
envelope.completed: variabel yang Anda kirim, dan nilai kolom setiap peran berdasarkan id kolom.
Header dan tanda tangan
Section titled “Header dan tanda tangan”Setiap pengiriman membawa:
| Header | Nilai |
|---|---|
webhook-id |
Id event (evt_…); sama pada setiap percobaan ulang. Gunakan untuk deduplikasi |
webhook-timestamp |
Detik Unix saat percobaan ini ditandatangani |
webhook-signature |
v1,<base64 HMAC-SHA256>, dipisahkan spasi jika lebih dari satu secret yang menandatangani |
user-agent |
BonaFide-Webhooks/1 |
Tanda tangan berupa HMAC-SHA256 atas {webhook-id}.{webhook-timestamp}.{raw body}, dengan kunci berupa bagian
secret setelah whsec_ yang telah didekode dari base64. Verifikasi body mentah (sebelum parsing JSON),
bandingkan dalam waktu konstan, dan tolak stempel waktu yang berselisih lebih dari lima menit dari jam Anda.
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);}Percobaan ulang
Section titled “Percobaan ulang”Pengiriman berhasil jika mendapat 2xx apa pun dalam 10 detik. Pengalihan tidak diikuti. Selain itu, pengiriman
dicoba ulang dengan backoff, setelah 5 detik, 5 menit, 30 menit, 2 jam, 5 jam, 10 jam, 14 jam, 20 jam, dan 24 jam,
paling lama tiga hari setelah event. Setelah itu pengiriman ditandai gagal.
- Jawab dengan cepat dan proses secara asinkron; lakukan deduplikasi berdasarkan
webhook-id. - Pengiriman dapat tiba tidak berurutan: periksa
data.envelope.statusalih-alih mengasumsikan urutan tertentu. GET /v1/webhook-endpoints/{id}/deliveriesmenampilkan percobaan terbaru beserta kode status, error, dan durasinya, dan setiap endpoint menampilkan tingkat keberhasilan 24 jamnya (health).POST /v1/events/{id}/redelivermengirim ulang event ke sebuah endpoint, misalnya setelah gangguan layanan.
Merotasi secret
Section titled “Merotasi secret”POST /v1/webhook-endpoints/{id}/secret/rotate langsung mengembalikan secret baru. Selama 24 jam berikutnya, setiap
pengiriman ditandatangani dengan secret baru dan secret lama (dua entri di webhook-signature), sehingga Anda
dapat men-deploy secret baru tanpa kehilangan event.