Agent skill
BoldSign Webhooks Skill
Receive and verify BoldSign webhooks. Use when setting up BoldSign e-signature webhook handlers, debugging X-BoldSign-Signature verification, answering the Verification event during webhook setup, or handling events like Sent, Viewed, Signed, Completed, Declined, Revoked, and Expired.
Install this skill
npx skills add hookdeck/webhook-skills --skill boldsign-webhooks
When to Use This Skill
- How do I receive BoldSign webhooks?
- How do I verify the
X-BoldSign-Signatureheader (t=...,s0=...,s1=...)? - How do I respond to the BoldSign
Verificationevent when saving a webhook? - How do I handle
Signed,Completed, orDeclinede-signature events? - Why is my BoldSign webhook signature verification failing?
Essential Code (USE THIS)
BoldSign signs every event with X-BoldSign-Signature:
X-BoldSign-Signature: t=1668693823, s0=9b7adf82...ca9
X-BoldSign-Signature: t=1668708521, s0=c10e26bf...54ab, s1=62a1abe1...21f6 (during key rollover)
tis the Unix timestamp (seconds) of the event; note the space after each comma.s0is HMAC-SHA256 (hex) of"<t>.<raw body>"keyed with the webhook signing secret (reveal it on the webhook's overview page).s1is the same HMAC made with the old key, present only while a rolled-over secret is still valid. Accept a match on either.- The event name is in
event.eventTypein the JSON body (also sent in theX-BoldSign-Eventheader). - Setup sends a
VerificationPOST (X-BoldSign-Event: Verification). Answer it with a200within 10 seconds; no event processing is needed.
Verification (Node.js)
const crypto = require('crypto');
// rawBody: Buffer of the exact request bytes (express.raw({ type: '*/*' }), not express.json)
function verifyBoldSign(rawBody, signatureHeader, secret) {
if (!secret || !signatureHeader) return false;
let timestamp;
const signatures = [];
for (const item of signatureHeader.split(',')) {
const [key, ...rest] = item.trim().split('=');
if (key === 't') timestamp = rest.join('=');
else if (key === 's0' || key === 's1') signatures.push(rest.join('='));
}
if (!/^\d+$/.test(timestamp ?? '') || signatures.length === 0) return false;
// Optional replay window, off by default: retries and replays keep the original timestamp
const expected = crypto.createHmac('sha256', secret)
.update(`${timestamp}.`).update(rawBody).digest('hex');
return signatures.some((sig) => {
const a = Buffer.from(sig), b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
}
Verification (Python)
import hashlib, hmac, re
def verify_boldsign(raw_body: bytes, signature_header, secret: str) -> bool:
if not secret or not signature_header:
return False
timestamp, signatures = None, []
for item in signature_header.split(","):
key, _, value = item.strip().partition("=")
if key == "t":
timestamp = value
elif key in ("s0", "s1"):
signatures.append(value)
if not timestamp or not re.fullmatch(r"[0-9]+", timestamp) or not signatures:
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(s.encode(), expected.encode()) for s in signatures)
BoldSign's docs suggest rejecting events older than 5 minutes. The examples leave that off by default, because Hookdeck retries and replays (and BoldSign's own retries) keep the original t. Set BOLDSIGN_ to a positive number to opt in. Use the event id for idempotency instead.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
The type is in event.eventType. Subscribe to the ones you need when creating the webhook.
| Event | Description |
|---|---|
Sent | A signature request is sent to recipients |
Viewed | A recipient views the document |
Signed | A recipient signs the document |
Completed | Signing completed by all recipients |
Declined | A recipient declines the signature request |
Revoked | Sender revokes the signature request |
Expired | The signature request expires without completion |
Others include Edited, Reassigned, Reminder, DeliveryFailed, SendFailed, AuthenticationFailed, identity verification and KBA events, sender identity events, and template events. See references/overview.md.
For the full event reference, see BoldSign available events.
Important Headers
| Header | Description |
|---|---|
X-BoldSign-Signature | t=<unix seconds>, s0=<hex>[, s1=<hex>] |
X-BoldSign-Event | Event type; Verification during webhook setup |
Environment Variables
BOLDSIGN_WEBHOOK_SECRET=your_webhook_signing_secret # "Reveal" on the webhook overview page
BOLDSIGN_TIMESTAMP_TOLERANCE_SECONDS=0 # optional; a positive value opts in to the replay window
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 boldsign --path /webhooks/boldsign
Reference Materials
- references/overview.md - BoldSign webhook concepts, events, payloads, retries
- references/setup.md - Creating and verifying a webhook, finding the signing secret
- references/verification.md - Signature verification details and gotchas