| name | documenso-webhooks-events |
| description | Implement Documenso webhook configuration and event handling.
Use when setting up webhook endpoints, handling document events,
or implementing real-time notifications for document signing.
Trigger with phrases like "documenso webhook", "documenso events",
"document completed webhook", "signing notification".
|
| allowed-tools | Read, Write, Edit, Bash(curl:*), Bash(ngrok:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Documenso Webhooks & Events
Overview
Configure and handle Documenso webhooks for real-time document signing notifications.
Prerequisites
- Documenso team account (webhooks require teams)
- HTTPS endpoint for webhook reception
- Understanding of webhook security
Supported Events
| Event | Trigger | Description |
|---|
document.created | Document created | New document added to system |
document.sent | Document sent | Document sent to recipients |
document.opened | Document opened | Recipient opened document |
document.signed | Recipient signed | One recipient completed signing |
document.completed | All signed | All recipients have signed |
document.rejected | Document rejected | Recipient rejected document |
document.cancelled | Document cancelled | Document was cancelled |
Webhook Setup
Step 1: Create Webhook in Dashboard
- Log into Documenso dashboard
- Click avatar -> "Team settings"
- Navigate to "Webhooks" tab
- Click "Create Webhook"
- Configure:
- URL: Your HTTPS endpoint
- Events: Select events to subscribe
- Secret: Optional but recommended
Step 2: Implement Webhook Endpoint
import express from "express";
import crypto from "crypto";
const app = express();
app.use("/webhooks/documenso", express.raw({ type: "application/json" }));
interface DocumensoWebhookPayload {
event:
| "document.created"
| "document.sent"
| "document.opened"
| "document.signed"
| "document.completed"
| "document.rejected"
| "document.cancelled";
payload: {
id: string;
title: string;
status: string;
createdAt: string;
updatedAt: string;
documentDataId: string;
userId: string;
teamId?: string;
recipients: Array<{
id: string;
email: string;
name: string;
role: string;
: ;
?: ;
}>;
};
: ;
: ;
}
app.(, (req, res) => {
receivedSecret = req.[] ;
expectedSecret = process..;
(expectedSecret && receivedSecret !== expectedSecret) {
.();
res.().({ : });
}
: ;
{
payload = .(req..());
} (error) {
.();
res.().({ : });
}
.();
.();
{
(payload);
res.().({ : });
} (error) {
.(, error);
res.().({ : });
}
});
(): <> {
{ event, payload } = webhook;
(event) {
:
(payload);
;
:
(payload);
;
:
(payload);
;
:
(payload);
;
:
(payload);
;
:
(payload);
;
:
(payload);
;
:
.();
}
}
Step 3: Event Handlers
async function onDocumentCreated(
doc: DocumensoWebhookPayload["payload"]
): Promise<void> {
console.log(`Document created: ${doc.title}`);
await db.documents.create({
externalId: doc.id,
title: doc.title,
status: "created",
createdAt: new Date(doc.createdAt),
});
}
async function onDocumentSent(
doc: DocumensoWebhookPayload["payload"]
): Promise<void> {
console.log(`Document sent: ${doc.title}`);
await db.documents.update({
where: { externalId: doc.id },
data: { status: "sent", sentAt: new Date() },
});
notifications.({
: ,
: ,
});
}
(): <> {
opener = doc..(
r. ===
);
.();
analytics.(, {
: doc.,
: opener?.,
});
}
(): <> {
signer = doc..( r. === );
.();
db..({
: doc.,
: signer?.,
: signer?. ? (signer.) : (),
});
allSigned = doc..(
r. === || r. ===
);
(allSigned) {
.();
}
}
(): <> {
.();
db..({
: { : doc. },
: { : , : () },
});
client = ();
signedDoc = client..({
: doc.,
});
storage.(, signedDoc);
workflows.(, {
: doc.,
: doc.,
});
}
(): <> {
rejecter = doc..( r. === );
.();
db..({
: { : doc. },
: { : , : rejecter?. },
});
notifications.({
: ,
: ,
: ,
});
}
(): <> {
.();
db..({
: { : doc. },
: { : , : () },
});
}
Step 4: Idempotency
const processedWebhooks = new Set<string>();
async function handleWebhookIdempotent(
webhook: DocumensoWebhookPayload
): Promise<boolean> {
const key = `${webhook.event}:${webhook.payload.id}:${webhook.createdAt}`;
if (processedWebhooks.has(key)) {
console.log(`Duplicate webhook ignored: ${key}`);
return false;
}
processedWebhooks.add(key);
if (processedWebhooks.size > 10000) {
const oldest = processedWebhooks.values().next().value;
processedWebhooks.delete(oldest);
}
await handleWebhookEvent(webhook);
return true;
}
import Redis ;
redis = (process..);
(): <> {
key = ;
result = redis.(key, , , , );
(!result) {
.();
;
}
(webhook);
;
}
Local Development
ngrok http 3000
Testing Webhooks
curl -X POST http://localhost:3000/webhooks/documenso \
-H "Content-Type: application/json" \
-H "X-Documenso-Secret: your-secret" \
-d '{
"event": "document.completed",
"payload": {
"id": "doc_test123",
"title": "Test Document",
"status": "COMPLETED",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T01:00:00Z",
"recipients": [
{
"id": "rec_123",
"email": "signer@example.com",
"name": "Test Signer",
"role": "SIGNER",
"signingStatus": "SIGNED"
}
]
},
"createdAt": "2024-01-01T01:00:00Z",
"webhookEndpoint": "https://yourapp.com/webhooks/documenso"
}'
Output
- Webhook endpoint configured
- All events handled
- Idempotency implemented
- Local testing ready
Error Handling
| Issue | Cause | Solution |
|---|
| 401 Unauthorized | Wrong secret | Check webhook secret |
| Webhook not received | URL not HTTPS | Use HTTPS endpoint |
| Duplicate processing | No idempotency | Add deduplication |
| Timeout | Slow handler | Use async queue |
Resources
Next Steps
For performance optimization, see documenso-performance-tuning.