Handling Webhooks
This page walks you through how to receive, validate, and process webhook messages securely and efficiently within your application.
Once you’ve set up a webhook subscription, Yousign will send HTTP POST requests to your configured endpoint whenever one of your subscribed events occurs.
This page explains how to handle these requests securely and reliably.
📦 Webhook Message Structure
Each webhook message has two parts:
1. HTTP Headers
Header | Description |
---|---|
Content-Type | Always application/json |
X-Yousign-Signature-256 | HMAC SHA-256 signature to verify payload authenticity |
X-Yousign-Retry | Retry count (0 for first delivery) |
X-Yousign-Issued-At | Timestamp when the webhook was sent |
User-Agent | Always Yousign Webhook Bot |
2. JSON Payload
{
"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",
...
}
}
}
Attribute | Value |
---|---|
event_id | A unique identifier for this webhook event. |
event_name | The name of the webhook event that was triggered. |
event_time | The timestamp (Unix epoch format) when the event occurred. It will stay constant even through retries. |
subscription_id | The unique identifier of the webhook subscription that received this event. |
subscription_description | Description of the webhook subscription. |
sandbox | A boolean indicating whether this event was triggered in a sandbox environment (true) or in production (false). |
data | Contains detailed information about the resources that triggered this event. The data varies depending on the event. Refer to the API Reference . |
🔐 Verifying Webhook Authenticity
To ensure the webhook is really from Yousign and hasn’t been tampered with, you must validate its signature.
Step-by-step:
-
Get the webhook secret key
- Found in the Yousign App when viewing your webhook subscription.
-
Compute a SHA-256 HMAC hash of the raw payload using your secret key.
-
Prefix the hash
with sha256=
. -
Compare it with the value of the X-Yousign-Signature-256 header.
Make sure to hash the raw request body—not a parsed or formatted version.
Examples:
// /!\ getallheaders() function does not work on all web servers.
$headers = getallheaders();
$expectedSignature = $headers['X-Yousign-Signature-256'] ?? null;
$payload = file_get_contents('php://input'); // This is an example, you should get the content from an HTTP request
$secret = 'SECRET_KEY_OF_YOUR_WEBHOOK';
$digest = hash_hmac('sha256', $payload, $secret);
$computedSignature = "sha256=" . $digest;
$doSignaturesMatch = hash_equals($expectedSignature, $computedSignature);
const express = require("express");
const crypto = require("crypto");
const https = require("https");
const app = express();
const secret = 'YOUR_WEBHOOK_SECRET_KEY';
const port = 3000;
// SSL Certification
const options = {
key: 'PRIVATE_KEY',
cert: 'CERTIFICATE',
};
/**
* If you are not using Express, you need to find a way to get the raw request body and
* encoding it.
* You can for example, use the `raw-body` middleware https://www.npmjs.com/package/raw-body
*/
app.use(
express.json({
verify: (req, res, buf, encoding) => {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || "utf-8");
}
},
})
)
app.post('/hook', (req, res) => {
const signature = Buffer.from(req.get('X-Yousign-Signature-256') || '', 'utf8');
const hmac = crypto.createHmac('sha256', secret);
const computedSignature = Buffer.from(`${'sha256' + '='}${hmac.update(req.rawBody).digest('hex')}`, 'utf8');
const isSignatureMatch = crypto.timingSafeEqual(signature, computedSignature)
})
https.createServer(options, app).listen(port);
import hmac
import hashlib
SECRET = "SECRET_KEY_OF_YOUR_WEBHOOK"
signature = "SIGNATURE_FROM_REQUEST_HEADERS_RECEIVED"
data = "DATA_FROM_REQUEST_PAYLOAD"
digest = hmac.new(SECRET.encode("utf-8"), data.encode("utf-8"), hashlib.sha256).hexdigest()
computed_signature = f"sha256={digest}"
matches = hmac.compare_digest(signature, computed_signature)
public class Main {
public static void main(String[] args) {
String payload = "..."
String signature = "sha256=0af379c7522c538c3f26a4f4399e55d7a1ed928420735a4538c73232587be9d6";
String SECRET = "2977fd4b627d28c6e54501b4e8a667ae";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hash = sha256_HMAC.doFinal(payload.getBytes());
StringBuilder result = new StringBuilder();
for (byte b : hash) {
result.append(String.format("%02x", b));
}
String computedSignature = "sha256=" + result.toString();
boolean matches = signature.equals(computedSignature);
}
}
📥 Best Practices for Processing Webhooks
Yousign webhooks are designed to be fast and reliable. To make the most of them:
Return a 2XX status quickly
Your server must return a 2xx HTTP status code within 1 second. If not:
- The delivery is considered failed
- The webhook will be retried
Even if you don’t want to process the event immediately, acknowledge it first, then process it asynchronously.
Be idempotent
You may receive the same webhook multiple times (due to retries or network issues).
Use the event_id
(which is globally unique) to deduplicate events.
Accept JSON
Your listener must accept application/json requests and handle variable payload structures depending on the event.
Handle retries gracefully
- Use the X-Yousign-Retry header to track retry attempts
- Always check the event_id to avoid duplicate processing
🚧 Timeouts and Failures
- If you respond after 1s on the first delivery attempt, it’s considered a timeout.
- Retry attempts are allowed up to 10s, but don’t rely on that grace period.
- Never assume a webhook was delivered just because your system is up—make sure to log them and monitor failures.
🧪 Testing Locally
For development, you can use tools like https://webhook.site to simulate your webhook listener:
- Create a webhook subscription with your temporary listener URL.
- Trigger a test Signature Request (in sandbox).
- View the payload and headers in your tool.
✅ Use sandbox mode to simulate events safely during development.
Remember to delete your webhook subscription after testing
Temporary endpoints like webhook.site expire and cause unnecessary failed calls if left active.
🔒 Security and IP Whitelisting
- HTTPS is required for all webhook endpoints.
- Self-signed certificates are not allowed.
- Private networks are not supported (e.g. 192.168.x.x, 10.x.x.x, 172.16.x.x).
- If needed, allow incoming traffic from:
- CIDR 5.39.7.128/28 (from IP 5.39.7.128 to IP 5.39.7.143)
- 52.143.162.31
- 51.103.81.166
Updated 7 days ago