Guide to Yousign Webhooks: Features and Best Practices
Yousign webhooks tell your systems how a signing process is going: a signature request is activated, a signer opens the link, signs or declines, an approver rejects, a request expires, or an identity check or electronic seal finishes. If you send contracts, mandates or onboarding documents through Yousign, the European e-signature platform, webhooks are how that progress reaches your CRM, document storage and back-office systems as it happens.
This guide covers how Yousign webhook subscriptions work, how deliveries are signed, the events you can subscribe to, the strict response window and retry schedule, and the practices that keep a Yousign receiver reliable.
What are Yousign webhooks?
A Yousign webhook subscription names the events you want, the environment they come from (sandbox or production) and the HTTPS endpoint to send them to. When a subscribed event happens, Yousign sends a JSON POST to that endpoint.
Yousign's developer documentation now lives under the Youtrust brand, at developers.youtrust.com, and its pages say "Youtrust" throughout. The wire format has not changed: the headers still start with x-yousign-, and the user agent is still Yousign Webhook Bot.
Every delivery is signed. Yousign computes an HMAC-SHA256 of the raw body with the subscription's secret key, encodes it as hex, prefixes it with sha256= and sends it in X-Yousign-Signature-256. A separate X-Yousign-Issued-At header carries the send time, but it is not part of the signature.
The detail that matters most is the response window. Yousign expects a 2xx within 1 second on the first attempt, so a handler that does real work before responding will fail, and its events will arrive later as retries.
Yousign webhook features
| Feature | Details |
|---|---|
| Configuration | The app (API > Webhooks) or the webhook subscription API |
| Subscription | Chosen events, or * for all events that exist at creation or update |
| Environments | Sandbox or production, chosen per subscription |
| Filters | scope (app, public_api or *) and workspace (IDs or *) |
| Quotas | 10 subscriptions on PLUS, 20 on PRO, 50 on SCALE |
| Signature header | X-Yousign-Signature-256: sha256=<hex> |
| Scheme | HMAC-SHA256, lowercase hex, sha256= prefix |
| Signed content | The raw request body |
| Secret | One per subscription, shown when you view it in the app |
| Other headers | X-Yousign-Retry (0 on first delivery), X-Yousign-Issued-At (not signed) |
| Response window | 1 second on the first attempt, 10 seconds on retries |
| Success | A 2xx or 3xx status, with no more than two redirects |
| Retries | Up to 8 when auto_retry is on, spread over about 4 days |
| Endpoint rules | HTTPS only, no self-signed certificates, no private networks |
| Source IPs | Published, see below |
If your endpoint is behind a firewall, Yousign lists the addresses to allow: 57.130.41.144/28, 51.38.96.112/28, 5.39.7.128/28, 52.143.162.31 and 51.103.81.166.
Common events
Yousign's webhook overview groups its events by product, and its API reference lists a few more that the overview doesn't yet include. Here are the main families:
| Area | Events | Fires when |
|---|---|---|
| Signature requests | signature_, .done, .declined, .expired, .canceled, .approved, .rejected, .reactivated, .deleted, .permanently_deleted, .reminder_executed, .automatic_, .paused, .resumed | A request moves between draft, approval, ongoing and its final states, is deleted or restored, or a reminder is sent |
| Signers | signer.notified, .link_opened, .done, .declined, .error, .sender_contacted, .notification_, .identification_, .identification_failed, .identification_blocked, .identification_expired, .identity_saved | A signer is invited, opens the link, signs, declines or hits an error, or passes or fails identification for AES and QES signatures |
| Approvers | approver.notified, .approved, .rejected, .notification_ | An approver is invited, approves or rejects, or their first email bounces |
| Document verification | document_analysis.done, verification.identity_, verification.identity_, verification.identity_, verification.proof_, verification.company.done, verification.bank_, verification.bank_, verification.watchlist.done, verification.signature_ | A verification has been processed or revised |
| Electronic seal | electronic_seal.done, electronic_seal.error | A seal was applied, or failed |
| Workflows | workflow_session.started, .blocked, .done, workflow_, .blocked, .done, applicant.notified, .processed, .session_blocked | A workflow session or action group changes status, or an applicant is invited, finishes or runs out of retries |
| Other | contact.created, user.completed, monitoring.natural_, .inconclusive, .canceled, .updated | A contact is created, a user completes their invitation, or ongoing monitoring of a person changes |
The events most integrations start with are signature_request.done (all required signers have signed), signer.done, signer.declined and signature_. One event, verification.bank_, can no longer be subscribed to but still reaches older subscriptions.
Every delivery shares one envelope:
{
"event_id": "b6c63685-c556-4a30-8fe9-b6f2b187d936",
"event_name": "signature_request.activated",
"event_time": "1670855889",
"subscription_id": "webhook-subscription-id",
"subscription_description": "My webhook for signed documents",
"sandbox": false,
"data": {
"signature_request": {
"id": "xxx-xxx",
"status": "approval"
}
}
}
event_id is globally unique, which makes it the deduplication key. event_time is when the event occurred, as a Unix timestamp in a string, and it stays the same across retries. sandbox tells you which environment the event came from. data depends on the event, and Yousign's API reference has a page per event describing it. Yousign's own example cuts data short, so treat the object above as a sketch of the envelope.
Setting up Yousign webhooks
- In the Yousign app, go to API > Webhooks and click Create a webhook.
- Enter your HTTPS endpoint URL and an optional description.
- Choose sandbox or production, then select the events to subscribe to.
- Click Create a webhook, open the new subscription, and copy its secret key into your server's configuration, for example as
YOUSIGN_WEBHOOK_SECRET.
To create subscriptions from code, use the webhook endpoints of the Public API v3. The same attributes are available there, including auto_retry. Set auto_retry to true: without it, Yousign only retries once, and only when the first attempt timed out.
If your API account is in trial mode, you can only create sandbox subscriptions, and only through the app. Changes to a subscription apply only to events triggered afterwards, and deleting one stops all deliveries straight away, including retries already in progress.
If you use Hookdeck, create a Yousign source, set the subscription's secret key as its Webhook Signing Secret, and use the source URL as the subscription endpoint. Yousign signs every delivery, so turn verification on.
Securing Yousign webhooks
Compute an HMAC-SHA256 of the raw request body with the secret key, encode it as hex, prepend sha256=, and compare the result with X-Yousign-Signature-256 in constant time. Yousign's documentation warns specifically against hashing a parsed or re-formatted body.
const crypto = require('crypto');
const express = require('express');
function verifyYousignSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
const expected =
'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const received = Buffer.from(signatureHeader.trim(), 'utf8');
const computed = Buffer.from(expected, 'utf8');
return received.length === computed.length && crypto.timingSafeEqual(received, computed);
}
const app = express();
app.post('/webhooks/yousign', express.raw({ type: '*/*' }), (req, res) => {
const secret = process.env.YOUSIGN_WEBHOOK_SECRET;
if (!secret) return res.sendStatus(500); // fail closed
if (!verifyYousignSignature(req.body, req.get('x-yousign-signature-256'), secret)) {
return res.sendStatus(401);
}
// Answer inside 1 second: store the raw event, then process it asynchronously.
const event = JSON.parse(req.body.toString('utf8'));
// Deduplicate on event.event_id, route on event.event_name.
res.sendStatus(200);
});
Yousign's own Node sample calls timingSafeEqual directly, which throws when the two values differ in length. The length check above turns that into a plain false.
The same check in Python:
import hashlib
import hmac
def verify_yousign_signature(raw_body: bytes, signature_header, secret: str) -> bool:
if not signature_header or not secret:
return False
digest = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
expected = f"sha256={digest}"
return hmac.compare_digest(signature_header.strip().encode("utf-8"), expected.encode("utf-8"))
# In FastAPI: raw_body = await request.body()
# verify_yousign_signature(raw_body, request.headers.get("x-yousign-signature-256"),
# os.environ["YOUSIGN_WEBHOOK_SECRET"])
To check your verifier, use the secret yousign-test-secret and this exact body:
{"event_id":"b6c63685-c556-4a30-8fe9-b6f2b187d936","event_name":"signature_request.done"}
The header should be:
sha256=e3af9225d895569b02f528f43ad8c0dcc69d051982805fa4173b69dd173cdb2e
X-Yousign-Issued-At is not covered by the signature, so an attacker who replays a captured request can set it to anything. Don't use it as a freshness check. Rely on deduplicating event_id instead.
Make Yousign webhooks production-ready. Hookdeck Event Gateway verifies the
X-Yousign-Signature-256header, answers Yousign well inside its 1-second window, and durably queues every signature request and signer event before your service sees it.
Yousign webhook limitations and pain points
A 1-second response window
The Problem: Deliveries fail even though your endpoint is up and returns 200.
Why It Happens: Yousign gives the first attempt 1 second. A response that arrives later counts as a failure even if it is a 2xx. Cold starts, a slow database write or a call to download the signed document are enough to miss it.
Workarounds:
- Verify, store the raw event and respond, with nothing else on the request path.
- Keep the endpoint warm if it runs on serverless infrastructure.
- Download documents and update records from a background job.
How Hookdeck Can Help: Hookdeck acknowledges Yousign as soon as the request arrives, then delivers to your service with its own timeout and retry policy, so the 1-second limit no longer applies to your code.
Retries depend on a subscription setting
The Problem: A failed delivery is never retried.
Why It Happens: Automatic retries only happen when the subscription's auto_retry is true. With it off, Yousign makes one extra attempt after a first-attempt timeout and none after an error status. With it on, it retries up to 8 times, after 2 minutes, 6 minutes, 30 minutes, 1 hour, 5 hours, 18 hours, 1 day and 2 days.
Workarounds:
- Set
auto_retrytotrueon every subscription. - Use
X-Yousign-Retryto monitor how often first attempts fail. - Reconcile important signature requests through the API after an outage.
How Hookdeck Can Help: Once Hookdeck has accepted an event, it retries delivery to your service on a schedule you choose and lets you replay failures, independent of Yousign's auto_retry setting.
Suspended endpoints
The Problem: Events stop arriving for a while after a run of failures.
Why It Happens: Yousign says it may temporarily suspend deliveries to an endpoint whose retries keep failing, queueing them until the endpoint is stable again. It does not publish the thresholds.
Workarounds:
- Treat repeated
X-Yousign-Retryvalues above0as an alert, before suspension happens. - Fix failing endpoints quickly, and expect a backlog once deliveries resume.
How Hookdeck Can Help: Hookdeck keeps answering Yousign while your service is down, so your outages don't build up the failure history that leads to suspension.
An unsigned timestamp
The Problem: There is no reliable way to reject an old, replayed request.
Why It Happens: Only the body is signed. X-Yousign-Issued-At sits outside the signature, and event_time inside the body is the event time, which stays the same through retries, so neither bounds a replay.
Workarounds:
- Deduplicate on
event_id. - Keep the endpoint on HTTPS and, where your network allows, restrict it to Yousign's published IP ranges.
How Hookdeck Can Help: Hookdeck's deduplication can drop repeated deliveries based on event_id before they reach your service.
Best practices
Verify against the raw body
The HMAC covers the exact bytes Yousign sent. In Express, use express.raw({ type: '*/*' }) and parse after verification. In Next.js, read await request.text(). In FastAPI, read await request.body().
Deduplicate on event_id
Yousign says you may receive the same webhook more than once, through retries or network issues. Store event_id before processing and skip anything you have already handled. See our guide to webhook idempotency.
Route on event_name, and check sandbox
Switch on event_name. If one endpoint receives both environments, check sandbox before acting, so a test signature never updates a production record.
Subscribe to what you handle
Subscribing with * covers every event that exists when you create or update the subscription, not events added later. List the events you actually handle, and review the list when Yousign adds new ones.
Acknowledge fast and process asynchronously
With a 1-second window, this is a requirement rather than a recommendation. See why to process webhooks asynchronously.
Conclusion
Yousign signs every webhook with sha256= and a hex HMAC-SHA256 of the raw body in X-Yousign-Signature-256, keyed with the subscription's secret. Verify against the raw bytes, deduplicate on event_id, route on event_name, turn auto_retry on, and respond within 1 second, because a slow response counts as a failure even when it succeeds.
Hookdeck Event Gateway sits in front of your receiver, checks the Yousign signature, answers inside the 1-second window, and holds each event until your service has processed it.
Get started with Hookdeck for free and handle Yousign webhooks reliably in minutes.