E-Signature Webhook Setup Guide (Adobe Sign)
Same for Adobe Sign.
E-Signature Webhook Setup Guide (Adobe Sign)
Adobe Sign webhooks
This guide walks you through the complete technical setup, from generating webhook
What You Need Before Starting
Adobe Sign Enterprise or Business Account
You need admin access to create webhooks
Public HTTPS Endpoint
Adobe Sign requires a publicly accessible URL with valid SSL. Local development URLs won't work. Use ngrok for testing or deploy to a staging server. Your endpoint must respond within 10 seconds or Adobe Sign will retry.
Webhook
Generate a 32-character random string for signature verification. Use this command:
openssl rand -hex 32
Store this secret in your environment variables. Never commit it to version control.
Backend Framework
This guide provides Node.js/Express examples, but the webhook
Step 1: Create the Webhook WebhookClick to read the full definition in our AI & Automation Glossary. in Adobe Sign
Log in to Adobe Sign and navigate to Account > Webhooks
Click "Create a webhook
" in the top right.webhookClick to read the full definition in our AI & Automation Glossary.Webhook
name: Use a descriptive identifier likeWebhookClick to read the full definition in our AI & Automation Glossary.production-client-onboardingorstaging-engagement-letters.Webhook
URL: Enter your public endpoint. Format:WebhookClick to read the full definition in our AI & Automation Glossary.https://yourdomain.com/webhooks/adobe-sign. Adobe Sign will POST JSON payloads to this URL.Webhook
notification URL: Leave blank unless you need a separate failure notification endpoint.WebhookClick to read the full definition in our AI & Automation Glossary.Events: Select these critical events for client onboarding:
AGREEMENT_CREATED- Agreement sent to signersAGREEMENT_ACTION_COMPLETED- Individual signer finishedAGREEMENT_WORKFLOW_COMPLETED- All signers completedAGREEMENT_RECALLED- Agreement cancelledAGREEMENT_EXPIRED- Agreement passed deadline
Webhook
scope: Choose "Account" to receive events for all agreements, or "Group" to limit to specific user groups.WebhookClick to read the full definition in our AI & Automation Glossary.Conditional parameters: Leave empty for now. Use this later to filter by agreement name patterns or custom fields.
Click "Save" and copy the Webhook
ID from the confirmation screen. You'll need this for troubleshooting.WebhookClick to read the full definition in our AI & Automation Glossary.
Step 2: Configure Webhook WebhookClick to read the full definition in our AI & Automation Glossary. Authentication
Adobe Sign uses HMAC-SHA256 signatures to verify webhook
Navigate to Account > Webhooks
Click "Generate new key" if this is your first setup.
Copy the signature key. This is different from your API integration key.
Store it as an environment variable:
export ADOBE_SIGN_WEBHOOK_SECRET="your_signature_key_here"
Adobe Sign sends this signature in the X-AdobeSign-ClientId header (despite the confusing name). Your code must verify every incoming request matches this signature.
Step 3: Build the Webhook WebhookClick to read the full definition in our AI & Automation Glossary. Receiver
Create an endpoint that validates signatures, parses events, and updates your systems.
Node.js/Express Implementation:
const express = require('express');
const crypto = require('crypto');
const app = express();
// Use raw body for signature verification
app.use('/webhooks/adobe-sign', express.raw({ type: 'application/json' }));
app.post('/webhooks/adobe-sign', async (req, res) => {
const signature = req.headers['x-adobesign-clientid'];
const webhookSecret = process.env.ADOBE_SIGN_WEBHOOK_SECRET;
// Verify signature
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(req.body)
.digest('base64');
if (signature !== expectedSignature) {
console.error('Webhook signature mismatch', {
received: signature,
expected: expectedSignature
});
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse the verified payload
const payload = JSON.parse(req.body.toString());
const event = payload.event;
const agreementId = payload.agreement?.id;
try {
switch (event) {
case 'AGREEMENT_CREATED':
await handleAgreementCreated(agreementId, payload);
break;
case 'AGREEMENT_ACTION_COMPLETED':
await handleSignerCompleted(agreementId, payload);
break;
case 'AGREEMENT_WORKFLOW_COMPLETED':
await handleAgreementCompleted(agreementId, payload);
break;
case 'AGREEMENT_RECALLED':
await handleAgreementCancelled(agreementId, payload);
break;
case 'AGREEMENT_EXPIRED':
await handleAgreementExpired(agreementId, payload);
break;
default:
console.log(`Unhandled event: ${event}`);
}
// Always return 200 within 10 seconds
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
// Still return 200 to prevent retries
res.status(200).json({ received: true, error: error.message });
}
});
async function handleAgreementCreated(agreementId, payload) {
// Update CRM: Set opportunity status to "Awaiting Signature"
await updateCRM({
agreementId: agreementId,
status: 'sent',
sentDate: payload.agreement.createdDate,
signers: payload.agreement.participantSets.map(p => p.memberInfos[0].email)
});
}
async function handleSignerCompleted(agreementId, payload) {
const completedSigner = payload.participantSet.memberInfos[0].email;
// Log individual signer completion
await logSignerActivity({
agreementId: agreementId,
signerEmail: completedSigner,
completedAt: payload.actingUser.actingUserDate,
ipAddress: payload.actingUser.ipAddress
});
// Send internal notification
await notifyTeam(`${completedSigner} signed agreement ${agreementId}`);
}
async function handleAgreementCompleted(agreementId, payload) {
// Download signed PDF
const signedPdf = await downloadSignedDocument(agreementId);
// Store in document management system
await storeFinalDocument({
agreementId: agreementId,
fileName: payload.agreement.name,
pdfBuffer: signedPdf,
completedDate: payload.agreement.completedDate
});
// Update CRM: Move opportunity to "Closed Won"
await updateCRM({
agreementId: agreementId,
status: 'fully_executed',
completedDate: payload.agreement.completedDate
});
// Trigger onboarding workflow
await startClientOnboarding(agreementId);
}
async function handleAgreementCancelled(agreementId, payload) {
await updateCRM({
agreementId: agreementId,
status: 'cancelled',
cancelledBy: payload.actingUser.email,
cancelledDate: payload.actingUser.actingUserDate
});
}
async function handleAgreementExpired(agreementId, payload) {
await updateCRM({
agreementId: agreementId,
status: 'expired',
expirationDate: payload.agreement.expirationDate
});
// Alert account manager
await notifyAccountManager(agreementId, 'Agreement expired without completion');
}
app.listen(3000);
Critical Implementation Notes:
Use express.raw() instead of express.json() for the webhook
Always return HTTP 200, even if your internal processing fails. Returning 4xx or 5xx triggers Adobe Sign retries, which can flood your system. Log errors internally and handle them asynchronously.
Set a 9-second timeout on all database and API
Step 4: Test the Integration
Local Testing with ngrok:
ngrok http 3000
Copy the HTTPS URL (e.g., https://abc123.ngrok.io) and update your Adobe Sign webhookhttps://abc123.ngrok.io/webhooks/adobe-sign.
Send a Test Webhook
In Adobe Sign, go to your webhookAGREEMENT_CREATED event. Check your server logs for the incoming payload.
End-to-End Test:
- Create a test agreement in Adobe Sign with your email as the signer.
- Sign the document.
- Verify your webhookreceivedwebhookClick to read the full definition in our AI & Automation Glossary.
AGREEMENT_ACTION_COMPLETEDandAGREEMENT_WORKFLOW_COMPLETEDevents. - Check that your CRMor database updated correctly.CRMClick to read the full definition in our AI & Automation Glossary.
Common Issues:
Signature verification fails: Ensure you're using the raw request body, not the parsed JSON. The signature is calculated on the exact bytes Adobe Sign sent.
Webhook
Missing events: Check your webhookAGREEMENT_MODIFIED) aren't enabled by default.
Step 5: Handle Webhook WebhookClick to read the full definition in our AI & Automation Glossary. Retries
Adobe Sign retries failed webhooks
- Immediate retry
- 5 minutes
- 15 minutes
- 1 hour
- 6 hours
- 24 hours
Implement idempotency to handle duplicate events. Use the webhookNotificationId field as a unique identifier:
const processedWebhooks = new Set();
app.post('/webhooks/adobe-sign', async (req, res) => {
const payload = JSON.parse(req.body.toString());
const notificationId = payload.webhookNotificationId;
if (processedWebhooks.has(notificationId)) {
console.log(`Duplicate webhook ${notificationId}, skipping`);
return res.status(200).json({ received: true, duplicate: true });
}
processedWebhooks.add(notificationId);
// Process event...
});
For production systems, store processed notification IDs in Redis or your database with a 7-day TTL.
Step 6: Monitor Webhook WebhookClick to read the full definition in our AI & Automation Glossary. Health
Adobe Sign provides webhook
Check this weekly for:
- Failed deliveries (4xx/5xx responses)
- Slow response times (>5 seconds)
- Disabled webhooks(Adobe Sign auto-disables after 100 consecutive failures)webhooksClick to read the full definition in our AI & Automation Glossary.
Set up internal monitoring:
const webhookMetrics = {
received: 0,
processed: 0,
failed: 0,
avgProcessingTime: 0
};
// Log metrics every hour
setInterval(() => {
console.log('Webhook metrics:', webhookMetrics);
// Send to your monitoring service (Datadog, CloudWatch, etc.)
}, 3600000);
Production Checklist
Before going live:
- [ ] Webhooksecret stored in environment variables, not codeWebhookClick to read the full definition in our AI & Automation Glossary.
- [ ] HTTPS endpoint with valid SSL certificate
- [ ] Signature verification implemented correctly
- [ ] Idempotency handling for duplicate events
- [ ] Response time under 9 seconds for all code paths
- [ ] Error logging to track processing failures
- [ ] Monitoring alerts for webhookdelivery failureswebhookClick to read the full definition in our AI & Automation Glossary.
- [ ] Tested with all selected event types
- [ ] Documented webhookURL and event mappings for your teamwebhookClick to read the full definition in our AI & Automation Glossary.
Adobe Sign webhooks

Reviewed by Revenue Institute
This guide is actively maintained and reviewed by the implementation experts at Revenue Institute. As the creators of The AI Workforce Playbook, we test and deploy these exact frameworks for professional services firms scaling without new headcount.
Revenue Institute
Need help turning this guide into reality? Revenue Institute builds and implements the AI workforce for professional services firms.