eSignature webhooks let SignWell notify your application when a document or template changes. After you register an HTTPS callback URL, SignWell sends JSON POST requests for events such as document_sent, document_viewed, document_signed, document_completed, and document_declined. Your application verifies the event, stores it, and triggers the next step without repeatedly polling the API.
Webhooks are the preferred pattern for real-time automation. Polling the Get Document endpoint can still be useful for reconciliation or as a fallback, but it creates more API traffic and usually detects changes later.
Why eSignature Webhooks Matter
A signature workflow rarely ends when the recipient clicks Submit. The completed agreement may need to update a CRM record, start onboarding, release a payment, provision an account, or notify an internal team. Webhooks turn each document change into an event that the rest of your stack can process.
- Update a contract or opportunity record when all recipients finish signing.
- Notify the sales, legal, or customer success channel when a document is completed or declined.
- Start provisioning, billing, or onboarding after a signed agreement is available.
- Track views and signatures without scheduling frequent status checks.
- Reconcile signature activity across a CRM, ATS, ERP, or internal workflow engine.
Available SignWell Webhook Events
SignWell posts document and template events to a registered webhook URL. The events most commonly used in application workflows are:
| Event | When it fires |
|---|---|
| document_sent | The document is sent to its recipients. |
| document_viewed | A signer views the document. This event is called each time a signer views it. |
| document_in_progress | The document starts being completed and moves to the pending API status. |
| document_signed | A recipient signs. Multi-recipient documents can generate one event per signer. |
| document_completed | All required recipients have signed, and the document is complete. |
| document_declined | A signer declines the document. |
| document_expired | The document expires before completion. |
| document_canceled | The sender cancels the document. |
| document_bounced | Email delivery to a signer bounces. |
| document_error | An error occurs while a document is created or updated. |
The full event catalog also includes document_created, document_recipients_updated, template_created, and template_error. For many integrations, document_completed is the main business trigger, while the other events support analytics, reminders, exception handling, and progress tracking.
Important: Do not treat document_viewed or document_signed as one-time document events. A document can be viewed repeatedly, and document_signed is emitted for each recipient who signs.
Register a Webhook
The SignWell API base URL is https://www.signwell.com/api/v1, and API requests use the X-Api-Key header. The Create Webhook endpoint accepts a callback_url and, when applicable, an optional api_application_id.
const response = await fetch(‘https://www.signwell.com/api/v1/hooks’, {
method: ‘POST’,
headers: {
‘X-Api-Key’: process.env.SIGNWELL_API_KEY,
‘Content-Type’: ‘application/json’,
‘Accept’: ‘application/json’
},
body: JSON.stringify({
callback_url: ‘https://yourapp.com/webhooks/signwell’
})
});
if (!response.ok) {
const body = await response.text();
throw new Error(`Webhook registration failed: ${response.status} ${body}`);
}
const webhook = await response.json();
const webhookId = webhook.id;
// Store webhookId securely for event verification.
Keep the returned webhook ID available to the receiving service. SignWell uses that ID as the HMAC key when your application verifies incoming events.
Register With the SignWell CLI
The official CLI requires Node.js 18 or newer. Install it, authenticate, and create the webhook resource:
npm install -g @signwell/cli
sw auth login –api-key YOUR_API_KEY
sw webhooks create –url https://yourapp.com/webhooks/signwell
The API reference currently documents callback URL registration rather than per-event filtering. A robust handler should therefore route only the event types it supports and safely ignore the rest.
Understand the Webhook Payload
SignWell sends the event metadata in the event and the document or template resource in data.object. For signer-specific events, event.related_signer can identify the signer associated with document_viewed, document_declined, or document_signed.
{
“event”: {
“hash”: “HMAC_HEX_VALUE”,
“time”: 1689332249,
“type”: “document_signed”,
“related_signer”: {
“email”: “[email protected]”,
“name”: “Jane Smith”
}
},
“data”: {
“object”: {
“id”: “DOCUMENT_UUID”,
“status”: “pending”
},
“account_id”: “ACCOUNT_UUID”
}
}
The data.object payload uses the same document or template shape returned by the corresponding API endpoint. Avoid destructuring a top-level document property because the resource is nested under data.object.
Verify SignWell Events With HMAC-SHA256
Every event should be verified before your application trusts its contents. In SignWell production webhooks, the expected signature is event.hash. The HMAC key is the webhook ID, and the signed message is the event type, an @ character, and the event time.
- HMAC key: the webhook ID returned when the webhook was created, or retrieved from the List Webhooks endpoint.
- Signed value: event.type + “@” + event.time.
- Expected signature: event.hash.
- Algorithm: HMAC-SHA256 with a constant-time comparison.
Use the Official Node SDK
npm install @signwell/node-sdk
import express from ‘express’;
import { Webhook } from ‘@signwell/node-sdk’;
const app = express();
app.use(express.json());
app.post(‘/webhooks/signwell’, async (req, res) => {
const webhookId = process.env.SIGNWELL_WEBHOOK_ID;
if (!webhookId) {
return res.status(500).send(‘Webhook ID is not configured’);
}
try {
Webhook.verifyEventOrThrow({
event: req.body.event,
webhookId,
toleranceSeconds: 300
});
} catch {
return res.status(401).send(‘Invalid SignWell event’);
}
try {
// jobs is your durable queue client.
await jobs.enqueue(‘signwell-event’, req.body);
} catch {
return res.status(503).send(‘Could not queue event’);
}
return res.sendStatus(200);
});
verifyEventOrThrow validates the event but does not store replay state. For handlers that create side effects, the official SDK also provides verifyEventOnceOrThrow. Use it with a shared atomic replay store backed by Redis, a database, or another system that can enforce uniqueness. The SDK memory replay store is intended for local development and single-process examples, not distributed production systems.
await Webhook.verifyEventOnceOrThrow({
event: req.body.event,
webhookId,
toleranceSeconds: 300,
replayStore
});
Manual Verification
A manual implementation should calculate the same HMAC and compare equal-length byte buffers in constant time:
import { createHmac, timingSafeEqual } from ‘node:crypto’;
function isValidSignWellEvent(event, webhookId) {
const expected = Buffer.from(event.hash, ‘hex’);
const actual = Buffer.from(
createHmac(‘sha256’, webhookId)
.update(`${event.type}@${event.time}`)
.digest(‘hex’),
‘hex’
);
return expected.length === actual.length &&
timingSafeEqual(expected, actual);
}
If you implement verification manually, also reject events that fall outside the acceptable timestamp window. SignWell production verification uses event.hash, not a raw-body HMAC in an x-signwell-signature request header, so ordinary JSON parsing does not invalidate the documented hash calculation.
Route Events Without Blocking the Webhook Request
The HTTP handler should verify the event, place it in durable storage or a queue, and return a successful response promptly. The worker can then perform slower tasks such as updating a CRM, downloading a completed PDF, or calling another service.
async function handleSignWellEvent(payload) {
const { event, data } = payload;
const resource = data.object;
switch (event.type) {
case ‘document_completed’:
await completeContractWorkflow(resource.id);
break;
case ‘document_declined’:
await notifyOwnerOfDecline(resource.id, event.related_signer);
break;
case ‘document_bounced’:
await flagDeliveryProblem(resource.id);
break;
default:
await recordEvent(event.type, resource.id);
}
}
Test eSignature Webhooks Locally
The SignWell CLI includes a local listener that prints webhook events and can forward them to another local service:
sw webhooks listen \
–port 3001 \
–forward http://localhost:4000/webhooks/signwell
SignWell still needs a publicly reachable callback URL. Expose port 3001 with a tunnel or run the listener in a public development environment, then register that public URL:
sw webhooks create –url https://YOUR-PUBLIC-URL.example/hooks
The listener is useful for inspecting and forwarding events, but it does not create a public tunnel. Your production endpoint should continue to verify event.hash with the webhook ID.
Production Patterns for Reliable Webhook Processing
- Use HTTPS and keep the webhook ID out of browser code, logs, and public repositories.
- Verify the hash and timestamp before reading document data or starting downstream work.
- Acknowledge the request only after the event is stored durably or accepted by a queue.
- Use replay-aware verification for side-effecting handlers in distributed systems.
- Make downstream actions idempotent. A useful application key can include the document ID, event type, event time, and related signer. Do not use only the document ID plus the event type.
- Log the event type, event time, resource ID, signer identity when present, verification result, processing result, and correlation identifiers.
- Monitor authentication failures, non-2xx responses, queue failures, and worker exceptions.
- Use the Get Document endpoint for periodic reconciliation when a downstream system must prove that its stored status matches SignWell.
Common eSignature Automation Patterns
CRM and Pipeline Updates
Use document_signed for signer-level progress and document_completed for the final stage change. Store the SignWell document ID on the CRM record so the event can be matched without relying on names or email subjects.
Provisioning and Onboarding
Treat document_completed as the trigger for account creation, access provisioning, implementation kickoff, or customer onboarding. Run these actions in a worker and protect them with an idempotency constraint.
Notifications and Exceptions
Send operational alerts for document_declined, document_bounced, and document_error. These events usually require a human decision rather than an automatic stage change.
Completed Document Retrieval
After document_completed, a worker can call the Completed PDF endpoint and attach the signed file to the appropriate record. The final file can take a few seconds to become available, so retrieval logic should tolerate a short generation delay.
Bulk-Send Tracking
Bulk sends create independent documents. Route each document event by its SignWell document ID, then aggregate those statuses into the parent campaign or batch record in your own system.
Conclusion
eSignature webhooks provide the event layer for reliable SignWell automation. Register an HTTPS callback, verify event.hash with the webhook ID, enforce timestamp and replay controls, store each event durably, and process business logic outside the request path. That structure supports real-time CRM updates, notifications, provisioning, reporting, and document retrieval without constant API polling.