Agent skill

Yousign Webhooks Skill

Receive and verify Yousign webhooks. Use when setting up Yousign webhook handlers, debugging X-Yousign-Signature-256 signature verification, or handling e-signature events like signature_request.done, signer.done, and signer.declined.

Install this skill

npx skills add hookdeck/webhook-skills --skill yousign-webhooks


Yousign (documentation now hosted under the Youtrust brand at developers.youtrust.com) sends an HTTP POST to your endpoint when e-signature events occur. The header names keep the x-yousign- prefix.

When to Use This Skill

  • How do I receive Yousign webhooks?
  • How do I verify the X-Yousign-Signature-256 header?
  • Why is my Yousign webhook signature verification failing?
  • How do I handle signature_request.done or signer.done events?
  • How do I deduplicate Yousign webhook retries?

Verification (core)

Yousign computes an HMAC-SHA256 (hex) of the raw request body, keyed with the webhook secret shown on your webhook subscription, and sends it as X-Yousign-Signature-256: sha256=<hex>. x-yousign-issued-at is a separate, unsigned timestamp; do not treat it as authenticated.

Node:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  if (!signatureHeader) return false;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

Python:

import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest((signature_header or "").encode(), expected.encode())

In Express use express.raw({ type: '*/*' }) so the body stays a raw Buffer whatever the content type.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

The event name is in the JSON body as event_name (there is no event-type header).

EventDescription
signature_request.activatedSignature request went from draft to ongoing
signature_request.doneAll required signers have signed
signature_request.declinedA signer declined the signature request
signer.doneA signer has signed
signer.declinedA signer declined (reason is in the payload)

Yousign also emits many more (approver, verification, electronic seal, workflow events). See references/overview.md.

Important Headers

HeaderDescription
X-Yousign-Signature-256sha256=<hex> HMAC-SHA256 of the raw body
X-Yousign-RetryRetry count (0 on first delivery)
X-Yousign-Issued-AtWhen the webhook was sent (not signed)

Header names are case-insensitive. Payload fields: event_id (globally unique, use it to deduplicate), event_name, event_time, subscription_id, subscription_description, sandbox, data.

Respond with a 2xx within 1 second, then process asynchronously.

Environment Variables

YOUSIGN_WEBHOOK_SECRET=your_webhook_subscription_secret

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 yousign --path /webhooks/yousign

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →