Skip to content
Seal
Esc
navigateopen⌘Jpreview
On this page

Webhooks

Receive real-time notifications for document lifecycle events

Webhooks

Webhooks allow your application to receive real-time HTTP notifications when events occur in your Seal account. Instead of polling the API, webhooks push event data to your server immediately.

How Webhooks Work

  1. Configure Endpoint: Create a webhook endpoint with your HTTPS URL
  2. Subscribe to Events: Choose which event types to receive
  3. Receive Notifications: Seal sends HTTP POST requests to your URL when events occur
  4. Verify Signatures: Validate webhook authenticity using HMAC-SHA256 signatures
  5. Respond Quickly: Return 200 OK within 5 seconds to acknowledge receipt

Available Events

Seal supports 17 webhook event types across three categories:

Document Events (7 events)

Event Description
document.created Document was created
document.sent Document was sent to recipients
document.viewed Document was viewed by any recipient
document.completed All recipients completed signing
document.voided Document was cancelled
document.expired Document passed its deadline unsigned
document.declined A recipient declined to sign

Recipient Events (6 events)

Event Description
recipient.added Recipient was added to document
recipient.viewed Recipient viewed the document
recipient.signed Recipient signed the document
recipient.approved Recipient approved the document
recipient.declined Recipient declined to sign
recipient.reminded Reminder was sent to recipient

Template Events (3 events)

Event Description
template.created Template was created
template.updated Template was modified
template.used Document was created from template

Webhook Payload Structure

All webhook payloads follow this structure:

{
  "id": "evt_1710288000000_a1b2c3d4",
  "type": "document.completed",
  "api_version": "2025-01-01",
  "created_at": "2026-03-12T12:00:00Z",
  "organization_id": "org_abc123",
  "data": {
    "document_id": "doc_xyz789",
    "title": "Service Agreement",
    "status": "completed",
    "recipients_count": 2,
    "signed_count": 2
  }
}

Payload Fields

Field Type Description
id string Unique event identifier (evt_{timestamp}_{random})
type string Event type (e.g., document.completed)
api_version string API version for this payload (2025-01-01)
created_at string ISO 8601 timestamp when event occurred
organization_id string Organization that owns this event
data object Event-specific data payload

Quick Start

1. Create Webhook Endpoint

const response = await fetch("https://api.seal.nyc/api/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: "Bearer ak_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://api.example.com/webhooks/seal",
    events: ["document.completed", "recipient.signed"],
  }),
});

const { secret } = await response.json();
// Save this secret securely - you'll need it for verification

2. Handle Webhook Requests

import { createHmac } from "crypto";

app.post("/webhooks/seal", async (req, res) => {
  // Verify signature
  const signature = req.headers["x-seal-signature"];
  const eventId = req.headers["x-seal-event-id"];
  const timestamp = req.headers["x-seal-timestamp"];
  const payload = JSON.stringify(req.body);

  const signedContent = `${eventId}.${timestamp}.${payload}`;
  const expectedSignature = `v1=${createHmac(
    "sha256",
    process.env.WEBHOOK_SECRET
  )
    .update(signedContent)
    .digest("hex")}`;

  if (signature !== expectedSignature) {
    return res.status(401).send("Invalid signature");
  }

  // Process event
  const event = req.body;

  switch (event.type) {
    case "document.completed":
      await handleDocumentCompleted(event.data);
      break;
    case "recipient.signed":
      await handleRecipientSigned(event.data);
      break;
  }

  // Respond quickly
  res.status(200).send("OK");
});

Best Practices

  1. Always Verify Signatures: Never trust webhook data without signature verification
  2. Use HTTPS: Webhook URLs must use HTTPS in production
  3. Handle Idempotency: Events may be delivered more than once - use event.id to deduplicate
  4. Return 200 Quickly: Acknowledge receipt immediately, process asynchronously
  5. Monitor Failures: Check webhook delivery logs for failed deliveries
  6. Rotate Secrets: Rotate webhook secrets every 90 days

Retry Behavior

If your endpoint doesn’t respond with 200 OK, Seal will retry delivery:

  • Retry Schedule: 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours
  • Maximum Attempts: 5 total attempts
  • Exponential Backoff: Increasing delays between retries
  • Abandonment: After 5 failed attempts, delivery is abandoned
  • Auto-Disable: After 10 consecutive endpoint failures, the endpoint is automatically disabled

Testing Webhooks

Local Development

Use tools like ngrok to expose your local server:

ngrok http 3000
# Use the HTTPS URL: https://abc123.ngrok.io/webhooks/seal

Manual Testing

Send test events from the Seal dashboard or use the API:

const response = await fetch(
  "https://api.seal.nyc/api/v1/webhooks/wh_abc123/test",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer ak_your_api_key_here",
    },
  }
);

Next Steps

Was this page helpful?