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
- Configure Endpoint: Create a webhook endpoint with your HTTPS URL
- Subscribe to Events: Choose which event types to receive
- Receive Notifications: Seal sends HTTP POST requests to your URL when events occur
- Verify Signatures: Validate webhook authenticity using HMAC-SHA256 signatures
- 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://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 verification2. 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.
- Always Verify Signatures: Never trust webhook data without signature verification
- Use HTTPS: Webhook URLs must use HTTPS in production
- Handle Idempotency: Events may be delivered more than once - use
event.idto deduplicate - Return 200 Quickly: Acknowledge receipt immediately, process asynchronously
- Monitor Failures: Check webhook delivery logs for failed deliveries
- 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/sealManual 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
- Webhook Setup - Detailed configuration guide
- Signature Verification - Security implementation
- Webhooks API - API reference
Last updated on