Agent skill
SignNow Webhooks Skill
Receive and verify SignNow webhooks (event subscriptions). Use when setting up a SignNow webhook handler, debugging X-SignNow-Signature verification, or handling e-signature events like document.complete, document.update, document.open, document.fieldinvite.signed and user.document.complete. Signing only happens if the subscription has a secret_key.
Install this skill
npx skills add hookdeck/webhook-skills --skill signnow-webhooks
SignNow is an e-signature API. Its webhooks (called event subscriptions) POST a JSON callback to your URL when documents, document groups or users change.
When to Use This Skill
- How do I receive SignNow webhooks?
- How do I verify the
X-SignNow-Signatureheader? - Why are my SignNow webhooks unsigned or failing signature verification?
- How do I handle
document.completeordocument.fieldinvite.signed? - What does the SignNow callback payload (
meta/content) look like?
Set a secret_key or Callbacks Are Unsigned
SignNow signs a callback only when the event subscription was created with a secret_key. It does not issue one: pass any string up to 300 characters in attributes.secret_key of POST /v2/event-subscriptions, or enter one (or click Generate New Key) in the API Dashboard. Without it there is no X-SignNow-Signature header, so verification (including Hookdeck's SignNow source) fails. Always set one.
Verification (core)
X-SignNow-Signature = base64 of HMAC-SHA256 over the raw body, keyed with the secret_key as UTF-8. No timestamp is part of the signed content.
const crypto = require('crypto');
function verifySignNowSignature(rawBody, signature, secret) {
if (!signature || !secret) return false;
const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8'))
.update(rawBody) // exact bytes received, not re-serialized JSON
.digest('base64'); // base64, not hex
const a = Buffer.from(signature, 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express: read the raw body for any Content-Type
// app.post('/webhooks/signnow', express.raw({ type: '*/*' }), handler)
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
SignNow has no webhook verification helper in its SDKs, so every example computes the HMAC directly (crypto in Node, hmac + hashlib in Python).
Common Event Types
The event name is meta.event; entity ids are in content.
| Event | Triggered when |
|---|---|
document.open | A document was opened |
document.update | A document was edited |
document.complete | All required fields on a document were filled in |
document.delete | A document was deleted |
document.fieldinvite.create / .sent | A field invite was created / sent |
document.fieldinvite.signed | A field invite was signed |
document.fieldinvite.decline | A field invite was declined |
user.document.complete | All required fields on a user's document were filled in |
document_group.complete | A document group was completed (or its invite expired) |
document_ | A signer completed all documents in a group invite |
The user.* and document_group.* families mirror these; see references/overview.md for the full list and payloads.
Environment Variables
# The secret_key you set on the SignNow event subscription
SIGNNOW_WEBHOOK_SECRET=your_event_subscription_secret_key
Local Development
npx hookdeck-cli listen 3000 signnow --path /webhooks/signnow
No account required — the CLI creates a guest account on first run and gives you a public HTTPS URL plus a web UI for inspecting requests. Use port 8000 for the FastAPI example.
Reference Materials
- references/overview.md — Event families, payload structure, retries
- references/setup.md — Creating an event subscription with a
secret_key - references/verification.md — Signature details, gotchas, debugging