Webhooks
Receive signed Esign events. Process retries safely.
A webhook tells your application when a signer or an Esign session changes
state. Suresign sends each webhook to the URL for your organization. The
webhook is an HTTPS POST request with a JSON event.
Set up a webhook
- Create a public HTTPS endpoint in your application.
- Open Webhooks in the Suresign dashboard. Save the endpoint URL. When you save the first URL, Suresign enables delivery and creates a signing secret.
- Select Reveal to see the signing secret. Store the secret securely on your server.
- Verify every request before processing its event.
Suresign accepts only public HTTPS URLs. Suresign rejects URLs that contain credentials. It also rejects redirects and URLs for non-public addresses. Delete the URL to stop delivery.
Event types
| Event | When it is sent |
|---|---|
esign_completed | The Esign session is complete. |
esign_failed | The Esign session fails. |
signer_completed | A signer completes signing. |
signer_failed | A signer fails signing. |
Only signer events include signer_id. See
Esign events and
signer events for the complete JSON bodies and
nested objects.
Request format
Each delivery has these headers:
| Header | Description | Example |
|---|---|---|
Content-Type | Request body media type. | application/json |
User-Agent | Suresign webhook user agent. | Suresign/1.0 |
Suresign-Webhook-Id | Stable delivery ID. | webhook_Wf3kQ8mN2rT6yP9x |
Suresign-Signature | Timestamp and HMAC signature. | t=1722776400,v1=6d46...8f89 |
Example body:
{
"attempt": 1,
"client_id": "webhook_xxx",
"data": {
"client_id": "esign_xxx",
"file_id": "file_xxx",
"status": "completed"
},
"esign_client_id": "esign_xxx",
"event_type": "signer_completed",
"org_id": "org_xxx",
"signer_id": "signer_xxx"
}Suresign sends compact JSON. Suresign sorts all keys alphabetically. Your code must read each field by its name. Always verify the signature against the raw request body before you parse the JSON.
Verify the signature
Suresign calculates v1 as a lowercase hexadecimal HMAC-SHA256 digest. The
digest input has this format:
{timestamp}.{raw_request_body}Use the exact UTF-8 bytes that your endpoint receives. Do not parse or serialize the JSON before verification. Compare the digests in constant time. Reject a timestamp that is outside your permitted time range. Use five minutes as the default range.
import crypto from "node:crypto";
export function verifySuresignWebhook(rawBody, signatureHeader, secret) {
const fields = Object.fromEntries(
signatureHeader.split(",").map((field) => field.split("=", 2)),
);
const timestamp = Number(fields.t);
const age = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
if (!Number.isSafeInteger(timestamp) || age > 300 || !fields.v1) {
return false;
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const supplied = Buffer.from(fields.v1, "hex");
const calculated = Buffer.from(expected, "hex");
return supplied.length === calculated.length &&
crypto.timingSafeEqual(supplied, calculated);
}Process events safely
- Verify the signature before parsing or acting on the payload.
- Use
client_idorSuresign-Webhook-Idas the idempotency key. Suresign can send the same delivery more than once. - Return a
2xxresponse without delay. Put slow work in a background queue. - Return a non-
2xxresponse if you cannot accept the request. - Do not return a redirect. Suresign treats a redirect as a failed delivery.
- Do not use the delivery order as the event order. Read the current Esign state if your business logic requires it.
Retries and delivery status
A delivery is successful when your endpoint returns a 2xx status.
Suresign retries a failed delivery for a maximum of seven days. The first
retry interval is one minute. The interval doubles after each retry, up to
12 hours.
Each retry has a new attempt value, timestamp, and signature. The client_id
does not change.
Use Webhooks in the Suresign
dashboard to see delivery status. A delivery has a pending, success, or
failed status. You can also send a stored event again. This action creates a
new delivery that links to the first delivery.
A new secret takes effect immediately. The new secret also applies to all pending retries. Change the secret in the Webhooks dashboard. Then, immediately update the secret on your server. Suresign retries deliveries that fail during this change.