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-Signature header?
  • Why are my SignNow webhooks unsigned or failing signature verification?
  • How do I handle document.complete or document.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.

EventTriggered when
document.openA document was opened
document.updateA document was edited
document.completeAll required fields on a document were filled in
document.deleteA document was deleted
document.fieldinvite.create / .sentA field invite was created / sent
document.fieldinvite.signedA field invite was signed
document.fieldinvite.declineA field invite was declined
user.document.completeAll required fields on a user's document were filled in
document_group.completeA document group was completed (or its invite expired)
document_group.invite.signer.completeA 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →