Webhooks guide

Webhooks

Signvoy delivers real-time event notifications to your server over HTTPS. Register an endpoint, verify the HMAC-SHA256 signature on every request, and react to signing events without polling.

Event types

Subscribe to one or more events when registering your endpoint. Pass an empty enabledEvents array to receive all current and future events.

EventDescription
document.sentDocument transitioned to sent state — signing links emailed to recipients
document.completedAll recipients have signed; signed PDF + certificate are ready for download
document.declinedA recipient declined to sign
document.voidedThe document owner voided the document
document.expiredThe document expired before all recipients signed
webhook.testFired by POST /v1/webhooks/:id/ping to verify the endpoint is reachable

Register an endpoint

Pass an HTTPS URL and the events you want to subscribe to.

curl
curl -X POST https://api.signvoy.com/v1/webhooks \
  -H "Authorization: Bearer $SIGNVOY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/signvoy",
    "description": "Production webhook",
    "enabledEvents": [
      "document.completed",
      "document.declined"
    ]
  }'
The response includes a secret field — a whsec_… prefixed HMAC-SHA256 signing secret. Store it securely — it is shown only once and is required to verify incoming deliveries.

Understand the payload

Every delivery is an HTTP POST with a JSON body:

payload.json
{
  "id": "evt_01j9kfqn4xpw8m5tzx2vdk3b7a",
  "type": "document.completed",
  "createdAt": "2026-07-03T10:45:22Z",
  "workspaceId": "ws_uuid...",
  "data": {
    "documentId": "doc_uuid...",
    "status": "completed",
    "completedAt": "2026-07-03T10:45:20Z"
  }
}

The id field is stable across retries — use it to deduplicate events on your end.

Verify the signature

Signvoy signs every delivery with HMAC-SHA256 and sends the signature in the Signvoy-Signature header. Always verify this before processing — never trust event payloads without verification.

verify-webhook.ts
import { createHmac, timingSafeEqual } from "node:crypto";

/** Verify a Signvoy webhook delivery.
 *  @param rawBody   The raw request body bytes (do NOT parse first).
 *  @param header    The "Signvoy-Signature" header value.
 *  @param secret    The whsec_… secret from webhook creation.
 */
export function verifySignvoyWebhook(
  rawBody: Buffer | string,
  header: string,
  secret: string,
): boolean {
  // Header format: "t=<timestamp>,v1=<sig>"
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=")),
  );
  const timestamp = parts["t"];
  const signature = parts["v1"];
  if (!timestamp || !signature) return false;

  // Re-derive the expected signature
  const payload = `${timestamp}.${rawBody.toString()}`;
  const expected = createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  // Timing-safe comparison prevents timing-oracle attacks
  return timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex"),
  );
}

// ── Express usage ────────────────────────────────────────────────
app.post(
  "/webhooks/signvoy",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const valid = verifySignvoyWebhook(
      req.body,
      req.headers["signvoy-signature"] as string,
      process.env.SIGNVOY_WEBHOOK_SECRET!,
    );
    if (!valid) return res.status(401).send("Invalid signature");

    const event = JSON.parse(req.body.toString());
    if (event.type === "document.completed") {
      // queue async processing here
    }
    res.status(200).send("OK");
  },
);

Retries and reliability

Signvoy retries failed deliveries up to 5 times with exponential back-off.
A delivery is considered failed if your server responds with a non-2xx status or times out (10 s).
Return 2xx as fast as possible — process the event asynchronously.
The Signvoy-Event-Id header is stable across retries for deduplication.
Inspect delivery history and manually retry from the dashboard or via POST /v1/webhooks/:id/deliveries/:deliveryId/retry.

Stop chasing signatures

Join thousands of teams using Signvoy to close deals, onboard clients, and sign contracts — faster.

No credit card · Free forever plan · 14-day Pro trial