Surepass

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

  1. Create a public HTTPS endpoint in your application.
  2. Open Webhooks in the Suresign dashboard. Save the endpoint URL. When you save the first URL, Suresign enables delivery and creates a signing secret.
  3. Select Reveal to see the signing secret. Store the secret securely on your server.
  4. 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

EventWhen it is sent
esign_completedThe Esign session is complete.
esign_failedThe Esign session fails.
signer_completedA signer completes signing.
signer_failedA 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:

HeaderDescriptionExample
Content-TypeRequest body media type.application/json
User-AgentSuresign webhook user agent.Suresign/1.0
Suresign-Webhook-IdStable delivery ID.webhook_Wf3kQ8mN2rT6yP9x
Suresign-SignatureTimestamp 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_id or Suresign-Webhook-Id as the idempotency key. Suresign can send the same delivery more than once.
  • Return a 2xx response without delay. Put slow work in a background queue.
  • Return a non-2xx response 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.

On this page