Seal Docs

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)

EventDescription
document.createdDocument was created
document.sentDocument was sent to recipients
document.viewedDocument was viewed by any recipient
document.completedAll recipients completed signing
document.voidedDocument was cancelled
document.expiredDocument passed its deadline unsigned
document.declinedA recipient declined to sign

Recipient Events (6 events)

EventDescription
recipient.addedRecipient was added to document
recipient.viewedRecipient viewed the document
recipient.signedRecipient signed the document
recipient.approvedRecipient approved the document
recipient.declinedRecipient declined to sign
recipient.remindedReminder was sent to recipient

Template Events (3 events)

EventDescription
template.createdTemplate was created
template.updatedTemplate was modified
template.usedDocument 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

FieldTypeDescription
idstringUnique event identifier (evt_{timestamp}_{random})
typestringEvent type (e.g., document.completed)
api_versionstringAPI version for this payload (2025-01-01)
created_atstringISO 8601 timestamp when event occurred
organization_idstringOrganization that owns this event
dataobjectEvent-specific data payload

Quick Start

1. Create Webhook Endpoint

const response = await fetch("https://seal.convex.site/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

Respond Quickly: Return 200 OK within 5 seconds. Process events asynchronously to avoid timeouts.

  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://seal.convex.site/api/v1/webhooks/wh_abc123/test", {
  method: "POST",
  headers: {
    Authorization: "Bearer ak_your_api_key_here",
  },
});

Next Steps

Last updated on

On this page