May 21, 2026 · Developer Guide

E-Signature Webhook Events: Know the Second a Document is Signed

Polling an API every 30 seconds to check if someone signed your contract is embarrassing. Here's how to set up webhooks that tell you the instant a document is viewed, signed, or completed — with HMAC verification to prevent spoofing.

Michael Beckett
Michael Beckett

Founder, Signbee

TL;DR

Signbee sends 4 webhook events: document.sent, document.viewed, document.signed, document.completed. Each is signed with HMAC-SHA256 so you can verify it's genuinely from Signbee. Retries happen automatically with exponential backoff. Compare that to DocuSign Connect, which requires XML parsing, envelope-level subscriptions, and a separate configuration portal.

Why webhooks, not polling

I've seen production codebases that poll DocuSign's API every 30 seconds to check if a document was signed. It's wasteful, it's slow (up to 30 seconds of latency), and it eats into your rate limits. If you're sending 100 documents per day and polling each one every 30 seconds until it's signed, that's thousands of unnecessary API calls.

Webhooks invert the model. The API calls you when something happens. Zero latency. Zero wasted requests. Your server receives a POST request within seconds of the event occurring, processes it, and moves on.

Signbee's webhook event types

Every document in Signbee progresses through a lifecycle, and each stage fires a webhook event:

EventWhen it fires
document.sentSigning email is delivered
document.viewedRecipient opens signing link
document.signedRecipient applies their signature
document.completedSigned PDF + certificate ready

The webhook payload

Every webhook event sends a JSON payload to your registered endpoint. Here's what a document.completed event looks like:

Example webhook payload — document.completed
{
  "event": "document.completed",
  "document_id": "doc_a1b2c3d4e5f6",
  "timestamp": "2026-05-21T14:32:18.000Z",
  "data": {
    "recipient_name": "Jane Smith",
    "recipient_email": "jane@example.com",
    "signer_ip": "203.0.113.42",
    "signed_at": "2026-05-21T14:32:15.000Z",
    "document_hash": "a3f2b8c1d4e5f678...",
    "signed_hash": "9c8d7e6f5a4b3c21...",
    "certificate_id": "cert_x9y8z7w6",
    "download_url": "https://signb.ee/api/v1/documents/doc_a1b2c3d4e5f6/download"
  }
}

The payload includes the SHA-256 hashes for both the original and signed document, so you can store them in your database for independent verification. For more on how the hashing works, see our audit trail deep-dive.

Verifying webhook signatures with HMAC-SHA256

Anyone can send a POST request to your webhook URL. Without verification, an attacker could forge a "document.completed" event and trick your system into thinking a contract was signed when it wasn't. This is why every webhook must be verified.

Signbee signs each webhook payload with HMAC-SHA256 using your webhook secret. The signature is included in the X-Signbee-Signature header.

Node.js — verifying Signbee webhook signatures
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

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

// Express.js example
app.post("/webhooks/signbee", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-signbee-signature"] as string;
  const rawBody = req.body.toString();

  if (!verifyWebhookSignature(rawBody, signature, process.env.SIGNBEE_WEBHOOK_SECRET!)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(rawBody);

  switch (event.event) {
    case "document.completed":
      // Archive signed PDF, update database, notify team
      handleDocumentCompleted(event.data);
      break;
    case "document.viewed":
      // Track engagement
      handleDocumentViewed(event.data);
      break;
  }

  res.status(200).json({ received: true });
});

Critical: Always use timingSafeEqual for signature comparison. Regular string comparison (===) leaks timing information that could allow an attacker to forge valid signatures character by character.

Sending a document and receiving the webhook

Here's the full flow — send a document via the API, then handle the webhook when it's signed:

Complete flow: send + webhook handler
// Step 1: Send the document
const response = await fetch("https://signb.ee/api/v1/send", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer sb_live_your_api_key",
  },
  body: JSON.stringify({
    markdown: "# Freelance Agreement\n\nThis agreement is between...",
    recipient_name: "Alex Chen",
    recipient_email: "alex@studio.co",
  }),
});

const { document_id } = await response.json();
// Store document_id in your database

// Step 2: Your webhook handler receives events automatically
// No polling. No cron jobs. No setTimeout loops.

Retry logic and failure handling

Your webhook endpoint might be temporarily down — deploying, restarting, or experiencing a network issue. Signbee handles this with automatic retries:

Attempt 1Immediate
Attempt 2+1 minute
Attempt 3+5 minutes
Attempt 4+30 minutes
Attempt 5+2 hours
Attempt 6 (final)+24 hours
Timeout10 seconds per attempt
Success criteriaAny 2xx response

If all retries fail, the event appears in your Signbee dashboard as "failed" and can be manually re-triggered.

How this compares to DocuSign Connect

DocuSign's webhook equivalent is called DocuSign Connect. It works, but the developer experience is significantly more complex:

AspectDocuSign ConnectSignbee Webhooks
Payload formatXMLJSON
ConfigurationAdmin console + API + per-envelopeDashboard URL + secret
Event granularityEnvelope-level (100+ event types)4 document-level events
Signature verificationHMAC (added 2023)HMAC-SHA256 (from day one)
Retry strategyConfigurable (complex)Automatic exponential backoff
Setup timeHoursMinutes

The biggest friction with DocuSign Connect is the XML payload format. In 2026, parsing XML in a Node.js application requires a third-party library (xml2js, fast-xml-parser) and adds complexity that JSON webhooks simply don't have.

Common webhook mistakes to avoid

Not responding with 200 quickly. Your webhook handler should return 200 immediately, then process the event asynchronously. If your handler takes longer than 10 seconds (e.g., downloading a PDF, sending emails), the delivery will be marked as failed and retried.

Not handling duplicates. Due to retries and network conditions, you may receive the same event more than once. Use the document_id + event combination as an idempotency key. If you've already processed it, return 200 and skip processing.

Not verifying signatures. Every webhook must be verified with HMAC-SHA256. Without verification, anyone who discovers your webhook URL can forge events. This is especially dangerous for document.completed events — a forged event could trick your system into provisioning access or closing a deal based on an unsigned document.

For the broader API architecture including webhooks, see our comprehensive webhooks setup guide and the API documentation.

Frequently Asked Questions

What webhook events does Signbee send?

Four events: document.sent, document.viewed, document.signed, and document.completed. Each includes the document ID, timestamp, and relevant metadata.

How do I verify webhook signatures?

Compute HMAC-SHA256 of the raw request body using your webhook secret, then compare to the X-Signbee-Signature header using a timing-safe comparison function. Never use === for signature comparison.

What if my webhook endpoint is down?

Signbee retries with exponential backoff: 1min, 5min, 30min, 2h, 24h. After 6 failed attempts, the event is marked as failed in your dashboard and can be manually re-triggered.

Real-time signing notifications — 5 free docs/month.

Last updated: May 21, 2026 · Michael Beckett is the founder of Signbee and B2bee Ltd.

Related resources