বিষয়বস্তুতে যান

খাম ও প্রাপক

একটি খাম হলো স্বাক্ষরের পথে থাকা একটি চুক্তি: আপনার ভেরিয়েবল দিয়ে রেন্ডার করা একটি টেমপ্লেট ভার্সন, যে প্রাপকেরা এর ওপর পদক্ষেপ নেন, এবং যা কিছু ঘটে তার প্রমাণ। POST /v1/envelopes দিয়ে একটি তৈরি করুন।

ফিল্ড ধরন মন্তব্য
template tpl_… আবশ্যক
version সংখ্যা বা "latest" ডিফল্ট latest; তৈরির সময় নির্ধারিত ও পিন করা হয়। প্রকাশিত হতে হবে
variables অবজেক্ট ভার্সনের JSON Schema অনুযায়ী যাচাই করা হয়
recipients অ্যারে প্রতিটি ভূমিকার জন্য একটি (role, name, email; ঐচ্ছিক externalUserId, verification, ফিল্ড id অনুযায়ী আগে থেকে পূরণের জন্য fields, স্বাক্ষরের ক্রম বদলাতে order)
subject, message স্ট্রিং স্বাক্ষরের ইমেইলে ব্যবহৃত হয় এবং স্বাক্ষর পৃষ্ঠায় দেখানো হয়
delivery email · embedded · none ডিফল্ট email
expiresAt RFC 3339 ভবিষ্যতের সময় হতে হবে; তখন খামের মেয়াদ শেষ হয়, এবং স্বাক্ষর ও দর্শক লিংক কাজ করা বন্ধ করে
cc অ্যারে টেমপ্লেটের CC ভূমিকার বাইরে অতিরিক্ত CC প্রাপক (name, email); সর্বোচ্চ 50
viewers অ্যারে যাঁরা স্বাক্ষর না করে খামটি অনুসরণ করেন (name, email, ঐচ্ছিক notifyOnSignature); প্রতি খামে সংখ্যা প্ল্যান অনুযায়ী সীমিত
viewerDocumentAccess বুলিয়ান দর্শকদের তাঁদের স্ট্যাটাস পৃষ্ঠায় নথি খুলতে দিন। ডিফল্ট false (শুধু স্ট্যাটাস); Team প্ল্যান ও তার ওপরে
reminders অবজেক্ট { "enabled": false } রিমাইন্ডার বন্ধ করে; { "enabled": true, "intervalHours": 24 } ব্যবধান বদলায় (1 থেকে 720)
externalId স্ট্রিং আপনার নিজের id, সর্বোচ্চ 200 অক্ষর
dedupeOn "external_id" আরেকটি তৈরি না করে এই externalId-যুক্ত বিদ্যমান খামটি ফেরত দিন
metadata স্ট্রিংয়ের অবজেক্ট প্রতিটি মান সর্বোচ্চ 500 অক্ষর; খামে ও ওয়েবহুকে ফেরত দেওয়া হয়

প্রত্যেক প্রাপকের ভার্সনের একটি ভূমিকা থাকতে হবে; প্রতিটি নন-CC ভূমিকার ঠিক একজন প্রাপক থাকতে হবে। এররগুলো একসঙ্গে জানানো হয়, /recipients/1/role বা /recipients/0/fields/start_date-এর মতো পয়েন্টারসহ।

রেসপন্স: খাম ও একটি Location হেডারসহ 201; dedupeOn মিললে বিদ্যমান খাম ও x-deduplicated: true-সহ 200; ?dry_run=true-এর জন্য ড্রাই-রান সারসংক্ষেপসহ 200; কোনো Free ওয়ার্কস্পেস তার অন্তর্ভুক্ত খাম ব্যবহার করে ফেললে 402; দর্শকের সংখ্যা প্ল্যানের সীমা ছাড়ালে বা viewerDocumentAccess প্ল্যানে না থাকলে 403 plan-upgrade-required (প্ল্যান ও সীমা)। ড্রাই রানও প্ল্যানের সীমা যাচাই করে।

প্রথম রিলিজের পরে যোগ করা প্রতিটি ফিল্ড ঐচ্ছিক: এগুলো ছাড়া পাঠানো অনুরোধ ঠিক আগের মতোই আচরণ করে।

টেমপ্লেট ঠিক করে কে কোন ক্রমে স্বাক্ষর করবেন। একটি খামের জন্য আপনি টেমপ্লেট না ছুঁয়েই তা বদলাতে পারেন:

{
"template": "tpl_9fK2mQ7xLp",
"variables": { "client": { "name": "Jane Doe" } },
"recipients": [
{ "role": "client", "name": "Jane Doe", "email": "jane@acme.dev", "order": 2 },
{ "role": "company", "name": "Sam Lee", "email": "sam@bonafide.dev", "order": 1 },
{ "role": "witness", "name": "Kim Park", "email": "kim@acme.dev", "order": 1 }
],
"cc": [{ "name": "Legal", "email": "legal@acme.dev" }],
"viewers": [{ "name": "Alex Chen", "email": "alex@acme.dev", "notifyOnSignature": true }],
"reminders": { "enabled": true, "intervalHours": 48 },
"expiresAt": "2026-11-30T23:59:59Z"
}
  • ক্রম: recipients[].order হলো সেই ধাপ, যেখানে প্রাপক স্বাক্ষর করেন; একই সংখ্যার প্রাপকেরা একই সময়ে স্বাক্ষর করেন। এটি না থাকলে টেমপ্লেট থেকে ভূমিকার order প্রযোজ্য হয়। ক্রম ধনাত্মক পূর্ণসংখ্যা; CC ভূমিকার ক্রম নির্ধারণ করা যায় না; প্রতিটি ভূমিকার এখনো তার প্রাপক লাগবে (যাতে টেমপ্লেটের ফিল্ডগুলোর স্বাক্ষরকারী থাকে); অন্তত একজন প্রাপককে স্বাক্ষর করতেই হবে। envelope.created ইভেন্টে routingOverridden: true রেকর্ড হয়।
  • CC: অতিরিক্ত CC প্রাপকেরা সম্পন্ন নথির ইমেইল পান এবং কখনো স্বাক্ষর করেন না। তাঁরা recipients-এ kind: "cc" ও ভূমিকা _cc-সহ দেখা যান।
  • রিমাইন্ডার: যাঁর পালা, তাঁকে প্রতি intervalHours অন্তর মনে করিয়ে দেওয়া হয় (ডিফল্ট: ডিপ্লয়মেন্টের REMINDER_INTERVAL_HOURS, 72), সর্বোচ্চ তিনবার; enabled: false কোনো রিমাইন্ডার পাঠায় না। রিমাইন্ডার email ডেলিভারিতে প্রযোজ্য।
  • স্বাক্ষরকারীর অথেন্টিকেশন প্রতিটি প্রাপকের জন্য আলাদা: verification ভূমিকার পদ্ধতিকে ওভাররাইড করে। none (শুধু অনন্য স্বাক্ষর লিংক, যে ভূমিকাগুলো কোনো পদ্ধতি নির্ধারণ করে না তাদের ডিফল্ট) এবং email_otp (লিংকের সঙ্গে ইমেইলে পাঠানো কোড) আজ উপলব্ধ; passkey ও id_check এখনো উপলব্ধ নয় বলে প্রত্যাখ্যান করা হয়। স্বাক্ষরকারী যাচাই দেখুন।
  • ভাষা: কোনো প্রাপক, CC বা দর্শকের ওপর locale (অথবা খামের ওপর, যাঁদের নিজস্ব ভাষা নেই তাঁদের সবার জন্য) তাঁদের স্বাক্ষর পৃষ্ঠা ও ইমেইলের ভাষা নির্ধারণ করে, যা 50টি স্বাক্ষর ভাষার একটি। এটি না থাকলে স্বাক্ষর পৃষ্ঠা ব্রাউজারের ভাষা অনুসরণ করে এবং ইমেইল ইংরেজিতে হয়।

ড্যাশবোর্ডের পাঠানোর স্ক্রিনেও একই বিকল্প আছে: ধাপগুলো আগে বা পরে সরান, একই সময়ে স্বাক্ষর করুন-এ টিক দিন, CC ও দর্শক যোগ করুন, প্রত্যেক স্বাক্ষরকারীর অথেন্টিকেশন (শুধু ইমেইল লিংক বা ইমেইল লিংক + কোড) ও ভাষা বেছে নিন, এবং মেয়াদ শেষের তারিখ ও রিমাইন্ডারের সময়সূচি ঠিক করুন।

  • email: প্রত্যেক প্রাপকের পালা এলে তাঁকে হোস্ট করা স্বাক্ষর পৃষ্ঠার লিংক ইমেইল করা হয়। যে প্রাপকেরা স্বাক্ষর করেননি, তাঁরা কয়েক দিন পরপর একটি রিমাইন্ডার পান (সর্বোচ্চ তিনটি), এবং খাম সম্পন্ন হলে সবাইকে ইমেইল করা হয়।
  • embedded: স্বাক্ষরকারীদের কোনো ইমেইল নয়। স্বাক্ষরকারী আপনার প্রোডাক্টে থাকলে একটি স্বাক্ষর সেশন তৈরি করুন। দর্শকদের তবুও তাঁদের স্ট্যাটাস লিংক ইমেইল করা হয়।
  • none: কোনো ইমেইলই নয় (দর্শকসহ); লিংক নিজে পৌঁছে দিলে এটি ব্যবহার করুন (হোস্টেড সেশন)।

POST /v1/envelopes/{id}/recipients/{recipient}/resend যে প্রাপকের পালা, তাঁকে একটি নতুন লিংক ইমেইল করে (সর্বোচ্চ মিনিটে একবার)।

┌──────────── declined (a signer declined)
│
sent ─────┼──────────── voided (POST /v1/envelopes/{id}/void)
│
├──────────── expired (expiresAt passed)
│
└──────────── completed ──► seal: pending ──► sealed
status অর্থ
rendering প্রস্তুত করা হচ্ছে
sent চলমান: প্রাপকেরা রাউটিং ক্রমে স্বাক্ষর করছেন
completed প্রত্যেক প্রাপক শেষ করেছেন। সিল করা শুরু হয়
declined একজন প্রাপক প্রত্যাখ্যান করেছেন; খামটি শেষ হয়
voided আপনি এটি বাতিল করেছেন ({ "reason": "…" }); খোলা স্বাক্ষর সেশনগুলো কাজ করা বন্ধ করে
expired সম্পন্ন হওয়ার আগে expiresAt পেরিয়ে গেছে

seal সিল করা PDF-এর অবস্থা জানায়: not_started, pending (সম্পন্ন, সিল করা হচ্ছে), sealed, অথবা failed। সিল করা কখনো সম্পন্ন হওয়াকে আটকায় না: খাম আগে সম্পন্ন হয় এবং তার ঠিক পরেই সিল করা হয়।

{
"id": "rcp_5Jw0pLs9Xe",
"role": "client",
"name": "Jane Doe",
"email": "jane@acme.dev",
"order": 1,
"status": "viewed",
"verification": "email_otp",
"completedAt": null
}

প্রাপকের status হলো pending (এখনো তাঁর পালা নয়), sent, viewed, completed বা declined, এবং kind হলো ভূমিকার signer, approver, form_filler বা cc। রাউটিং হয় order অনুযায়ী (ভূমিকার, অথবা পাঠানোর সময়ের ওভাররাইড): সর্বনিম্ন অসম্পূর্ণ ক্রমের সব প্রাপক একসঙ্গে পদক্ষেপ নেন, এবং তাঁরা শেষ করলে পরের দল শুরু হয়। CC প্রাপকেরা কখনো রাউটিং আটকান না।

একজন দর্শক স্বাক্ষর না করে একটি খাম অনুসরণ করেন: একজন ম্যানেজার, একজন প্যারালিগ্যাল, গ্রাহকের পক্ষে অ্যাকাউন্টের মালিক। দর্শকেরা প্রাপক নন। তাঁদের কোনো ভূমিকা বা পালা নেই, তাঁরা কখনো কিছু আটকান বা সম্পন্ন করেন না, এবং সনদের স্বাক্ষরকারীদের তালিকায় কখনো দেখা যান না।

  • খাম পাঠানো হলে (অথবা পরে POST /v1/envelopes/{id}/viewers দিয়ে তাঁদের যোগ করলে) প্রত্যেক দর্শক একটি শুধু-পড়ার স্ট্যাটাস পৃষ্ঠার লিংকসহ একটি ব্র্যান্ডেড ইমেইল পান: খামের শিরোনাম, প্রেরক, প্রতিটি ধাপ ও তার প্রাপকদের নাম, ভূমিকা, অবস্থা ও টাইমস্ট্যাম্প, যা মানুষ স্বাক্ষর করার সঙ্গে সঙ্গে লাইভ হালনাগাদ হয়। প্রাপকদের ইমেইল ঠিকানা দেখানো হয় না।
  • খাম সম্পন্ন, প্রত্যাখ্যাত বা বাতিল হলে দর্শকদের আবার ইমেইল করা হয়, এবং notifyOnSignature true হলে (ডিফল্টভাবে বন্ধ) প্রতিটি স্বাক্ষরের পরেও।
  • নথিটি পৃষ্ঠায় থাকে শুধু তখনই, যখন আপনি viewerDocumentAccess: true সেট করেন (Team প্ল্যান ও তার ওপরে): চলমান অবস্থায় মূল নথি, সিল হওয়ার পর সিল করা PDF। অন্যথায় দর্শকেরা শুধু স্ট্যাটাস দেখেন।
  • DELETE /v1/envelopes/{id}/viewers/{viewer} একজন দর্শকের প্রবেশাধিকার প্রত্যাহার করে: তাঁকে পাঠানো প্রতিটি লিংক সঙ্গে সঙ্গে কাজ করা বন্ধ করে।
  • লিংকগুলো ব্যক্তিগত ক্যাপাবিলিটি URL। প্রতিটি লিংক টোকেনের শুধু একটি SHA-256 সংরক্ষণ করা হয়। একটি লিংক কাজ করে খামের মেয়াদ শেষ না হওয়া পর্যন্ত (সর্বোচ্চ 90 দিন), খাম শেষ হওয়ার পর 30 দিন পর্যন্ত, এবং দর্শকের প্রবেশাধিকার প্রত্যাহারের পর কখনোই নয়।
  • যোগ করা, খোলা (পৃষ্ঠা ও নথির প্রথম দেখা) এবং প্রত্যাহার অডিট ট্রেইলে viewer.added, viewer.viewed, viewer.document_viewed ও viewer.revoked হিসেবে রেকর্ড হয়, আর তাই সনদেও।

একক-খামের রেসপন্সগুলো (create, get, void) তাঁদের তালিকা দেয়:

{
"viewers": [
{
"id": "vwr_2Hc8sQ1nVt",
"name": "Alex Chen",
"email": "alex@acme.dev",
"notifyOnSignature": true,
"status": "active",
"invitedAt": "2026-10-13T13:58:04.512Z",
"firstViewedAt": "2026-10-13T14:05:41.090Z",
"lastViewedAt": "2026-10-13T15:12:09.337Z",
"revokedAt": null,
"createdAt": "2026-10-13T13:58:04.512Z"
}
],
"viewerDocumentAccess": false
}

প্রতি খামে দর্শকের সংখ্যা প্ল্যানের ওপর নির্ভর করে (Free 1, Pay-as-you-go 2, Team 5, Business 20, Platform সীমাহীন); CC প্রাপকদের গোনা হয় না। প্রত্যাহার করা দর্শকের জায়গা খালি হয়ে যায়।

GET /v1/envelopes/{id}/document?version=… একটি PDF ডাউনলোড করে, যার SHA-256 থাকে x-document-sha256-এ:

version ফাইল
original (ডিফল্ট) রেন্ডার করা নথি, যেমনটি স্বাক্ষরের জন্য পাঠানো হয়েছিল
sealed চূড়ান্ত PDF: মানগুলো ফ্ল্যাট করা, সম্পন্নতার সনদ যুক্ত, ডকুমেন্ট টাইমস্ট্যাম্প। envelope.sealed-এর পরে
certificate শুধু সম্পন্নতার সনদ

খামের documentHash হলো মূল নথির SHA-256 এবং sealedDocumentHash সিল করা ফাইলেরটি; যাচাই এগুলোই খোঁজে।

প্রতিটি কাজ প্রতি-খাম একটি হ্যাশ চেইনে একটি ইভেন্ট: hash = SHA-256(prevHash ‖ canonical JSON of the event), যেখানে প্রথম prevHash হলো 64টি শূন্য। GET /v1/envelopes/{id}/events ইভেন্টগুলো ফেরত দেয় এবং চেইনটি যাচাই করে:

{
"data": [
{
"id": "evt_…",
"envelope": "env_8Gm2kQ4rT1",
"seq": 1,
"type": "envelope.created",
"createdAt": "2026-10-13T13:58:04.512Z",
"recipient": null,
"actor": { "kind": "api_key", "id": "…", "name": null, "email": null },
"ip": "203.0.113.7",
"userAgent": null,
"data": { "template": "tpl_…", "templateVersion": 3, "documentHash": "9f2c…" },
"hash": "4b1d…",
"prevHash": "0000000000000000000000000000000000000000000000000000000000000000"
}
],
"chain": { "valid": true, "brokenAt": null }
}

ওয়েবহুক ইভেন্টের ধরন ছাড়াও ট্রেইলে শুধু-অডিটের এন্ট্রি থাকে, যেমন session.created, recipient.verified, recipient.reminded এবং দর্শক ইভেন্ট (দর্শক নিজের পৃষ্ঠা খুললে actor kind হয় viewer)।

UI-তে লাইভ স্ট্যাটাসের জন্য GET /v1/envelopes/{id}/events/stream একটি সার্ভার-সেন্ট ইভেন্ট স্ট্রিম: প্রতিটি ফ্রেমের ইভেন্টের নাম হলো ইভেন্টের type এবং এর id হলো seq; আবার শুরু করতে Last-Event-ID দিয়ে পুনরায় সংযোগ করুন।

GET /v1/envelopes ফিল্টার করে status, seal, template এবং q (প্রাপকের নাম ও ইমেইল, externalId এবং খামের id-তে মুক্ত টেক্সট অনুসন্ধান) দিয়ে। প্রতিটি তালিকার মতো এটিও কার্সর দিয়ে পেজিনেট করা:

{ "data": [ … ], "hasMore": true, "nextCursor": "…" }

nextCursor-কে cursor হিসেবে ফেরত পাঠান; limit 1 থেকে 100 (ডিফল্ট 20)।