ওয়েবহুক
আপনি যে খাম ইভেন্টে সাবস্ক্রাইব করেন, তার প্রতিটির জন্য Bona Fide আপনার এন্ডপয়েন্টে একটি HTTPS POST পাঠায়। ডেলিভারিগুলো
Standard Webhooks স্পেসিফিকেশন মেনে চলে, তাই এর যেকোনো লাইব্রেরি এগুলো যাচাই করতে পারে।
একটি এন্ডপয়েন্ট নিবন্ধন করা
“একটি এন্ডপয়েন্ট নিবন্ধন করা” শিরোনামের অংশসেটিংস › ওয়েবহুক-এ, অথবা org:manage
স্কোপযুক্ত একটি API কী দিয়ে:
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"] }'রেসপন্সে secret (whsec_…) থাকে, শুধু এই একবারই। খালি events তালিকা প্রতিটি ইভেন্টে সাবস্ক্রাইব করে।
URL-টি পাবলিক https:// হতে হবে। একটি এন্ডপয়েন্ট একটি মোডের: টেস্ট কী দিয়ে তৈরি এন্ডপয়েন্ট শুধু
টেস্ট মোডের খামের ইভেন্ট পায়, লাইভগুলো শুধু লাইভ ইভেন্ট।
PATCH /v1/webhook-endpoints/{id} URL, ইভেন্ট বা বিবরণ বদলায় এবং এন্ডপয়েন্টটি থামায় বা আবার চালু করে
(enabled)। POST /v1/webhook-endpoints/{id}/test একটি webhook.test পিং পাঠায়।
ইভেন্ট
“ইভেন্ট” শিরোনামের অংশ| ধরন | কখন |
|---|---|
envelope.created |
খামটি তৈরি হয়েছে |
envelope.ready |
নথি রেন্ডার হয়েছে এবং খামটি এর প্রাপকদের জন্য প্রস্তুত |
recipient.sent |
একজন প্রাপকের পালা এসেছে (এবং ইমেইল ডেলিভারিতে, তাঁর লিংক পাঠানো হয়েছে) |
recipient.viewed |
একজন প্রাপক প্রথমবার স্বাক্ষর পৃষ্ঠা খুলেছেন |
recipient.signed |
একজন প্রাপক জমা দিয়েছেন (অথবা স্বয়ংক্রিয়ভাবে প্রতিস্বাক্ষরিত হয়েছে) |
recipient.declined |
একজন প্রাপক প্রত্যাখ্যান করেছেন |
envelope.completed |
প্রত্যেক প্রাপক শেষ করেছেন। data.values-এ ভেরিয়েবল ও টাইপ করা প্রতিটি মান থাকে |
envelope.sealed |
সিল করা PDF ডাউনলোডের জন্য প্রস্তুত |
envelope.declined |
একজন প্রাপক প্রত্যাখ্যান করায় খামটি শেষ হয়েছে |
envelope.voided |
আপনি খামটি বাতিল করেছেন |
envelope.expired |
সম্পন্ন হওয়ার আগে expiresAt পেরিয়ে গেছে |
signing_link.used |
একজন ভিজিটর সবসময় চালু একটি স্বাক্ষর লিংক থেকে খাম শুরু করেছেন |
acceptance.created স্কিমায় সংরক্ষিত, এখনো নির্গত হয় না।
{ "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 হলো ইভেন্টের সময়ের পূর্ণ খাম। data.values শুধু
envelope.completed-এ থাকে: আপনার পাঠানো ভেরিয়েবল, এবং ফিল্ড id অনুযায়ী প্রতিটি ভূমিকার ফিল্ডের মান।
হেডার ও স্বাক্ষর
“হেডার ও স্বাক্ষর” শিরোনামের অংশপ্রতিটি ডেলিভারিতে থাকে:
| হেডার | মান |
|---|---|
webhook-id |
ইভেন্টের id (evt_…); প্রতিটি রিট্রাইয়ে একই। ডুপ্লিকেট বাদ দিতে এটি ব্যবহার করুন |
webhook-timestamp |
এই প্রচেষ্টা যখন স্বাক্ষরিত হয়েছে, সেই Unix সেকেন্ড |
webhook-signature |
v1,<base64 HMAC-SHA256>, একাধিক সিক্রেট স্বাক্ষর করলে স্পেস দিয়ে আলাদা |
user-agent |
BonaFide-Webhooks/1 |
স্বাক্ষরটি হলো {webhook-id}.{webhook-timestamp}.{raw body}-এর ওপর HMAC-SHA256, যার কী হলো সিক্রেটের whsec_-এর পরের অংশের
base64-ডিকোড করা রূপ। কাঁচা বডি যাচাই করুন (JSON পার্স করার আগে), কনস্ট্যান্ট টাইমে তুলনা করুন, এবং
আপনার ঘড়ি থেকে পাঁচ মিনিটের বেশি দূরের টাইমস্ট্যাম্প প্রত্যাখ্যান করুন।
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);}রিট্রাই
“রিট্রাই” শিরোনামের অংশ10 সেকেন্ডের মধ্যে যেকোনো 2xx পেলে ডেলিভারি সফল। রিডাইরেক্ট অনুসরণ করা হয় না। অন্য সবকিছু ব্যাকঅফসহ রিট্রাই
করা হয়: 5 সেকেন্ড, 5 মিনিট, 30 মিনিট, 2 ঘণ্টা, 5 ঘণ্টা, 10 ঘণ্টা, 14 ঘণ্টা, 20 ঘণ্টা ও 24 ঘণ্টা পরে,
ইভেন্টের পর সর্বোচ্চ তিন দিন পর্যন্ত। তারপর ডেলিভারিটি ব্যর্থ হিসেবে চিহ্নিত হয়।
- দ্রুত উত্তর দিন এবং অ্যাসিঙ্ক্রোনাসভাবে প্রসেস করুন;
webhook-idদিয়ে ডুপ্লিকেট বাদ দিন। - ডেলিভারি ক্রমের বাইরে আসতে পারে: কোনো ক্রম ধরে না নিয়ে
data.envelope.statusযাচাই করুন। GET /v1/webhook-endpoints/{id}/deliveriesসাম্প্রতিক প্রচেষ্টাগুলো স্ট্যাটাস কোড, এরর ও সময়সহ তালিকা দেয়, এবং প্রতিটি এন্ডপয়েন্ট তার 24 ঘণ্টার সাফল্যের হার (health) দেখায়।POST /v1/events/{id}/redeliverএকটি ইভেন্ট আবার একটি এন্ডপয়েন্টে পাঠায়, যেমন কোনো বিভ্রাটের পরে।
সিক্রেট রোটেট করা
“সিক্রেট রোটেট করা” শিরোনামের অংশPOST /v1/webhook-endpoints/{id}/secret/rotate সঙ্গে সঙ্গে একটি নতুন সিক্রেট ফেরত দেয়। পরের 24 ঘণ্টা প্রতিটি
ডেলিভারি নতুন ও পুরোনো দুটি সিক্রেট দিয়েই স্বাক্ষরিত হয় (webhook-signature-এ দুটি এন্ট্রি), যাতে আপনি
কোনো ইভেন্ট না হারিয়ে নতুন সিক্রেট ডিপ্লয় করতে পারেন।