Webhooks
Receive real time events when a signer signs and when a document is fully signed, verified with an HMAC signature.
Webhooks let SignYu notify your server when signing events happen, so you do not have to poll. We send an HTTP POST with a JSON body to each endpoint you configure.
Configuring endpoints
Add and manage webhook endpoints from your dashboard under Developers. Each endpoint has:
- A HTTPS URL that receives the events.
- A signing secret (shown in the dashboard) used to verify requests.
- An enabled toggle so you can pause delivery without deleting the endpoint.
Every enabled endpoint on your account receives all events.
Events
| Event | When it fires |
|---|---|
signer.signed | A single signer has completed signing. |
document.completed | Every signer has signed and the document is complete. |
Payload
Each delivery is a JSON body. signer.signed includes the signer who just signed; both events include the full list of signers.
{
"event": "signer.signed",
"documentId": "b6b1f0e2-1c9a-4a1e-9b1a-2f3d4e5a6b7c",
"status": "SENT",
"occurredAt": "2026-07-17T07:05:00.000Z",
"signer": {
"signerId": "s_1",
"name": "Asha Rao",
"email": "asha@example.com",
"signingOrder": 1,
"signedAt": "2026-07-17T07:05:00.000Z",
"hasSigned": true
},
"signers": [
{
"signerId": "s_1",
"name": "Asha Rao",
"email": "asha@example.com",
"signingOrder": 1,
"signedAt": "2026-07-17T07:05:00.000Z",
"hasSigned": true
}
]
}
Headers
| Header | Description |
|---|---|
X-SignSetu-Event | The event type, for example signer.signed. |
X-SignSetu-Signature | sha256= followed by the HMAC signature of the raw request body. |
Content-Type | Always application/json. |
Verifying the signature
Compute an HMAC-SHA256 of the raw request body using your endpoint's signing secret, then compare it to the value in the X-SignSetu-Signature header. Always compare using a constant time function, and use the raw body bytes, not a re-serialized object.
import crypto from "crypto";
function verifySignature(rawBody, header, secret) {
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(header);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Example handler using Express with the raw body preserved:
app.post(
"/webhooks/signyu",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.header("X-SignSetu-Signature");
if (!verifySignature(req.body, signature, process.env.SIGNYU_WEBHOOK_SECRET)) {
return res.status(400).send("Invalid signature");
}
const payload = JSON.parse(req.body.toString());
// handle payload.event ...
res.status(200).send("ok");
}
);
Responding and retries
Return a 2xx status to acknowledge receipt. If your endpoint returns a non 2xx status or times out (after 10 seconds), delivery is retried a few times with backoff. Make your handler idempotent, since the same event may be delivered more than once.