E-Signature Webhook Setup Guide (Adobe Sign)
Same for Adobe Sign.
E-Signature Webhook Setup Guide (Adobe Sign)
Adobe Sign webhooks webhooksClick to read the full definition in our AI & Automation Glossary. push real-time notifications to your systems when clients sign documents, complete agreements, or trigger other signature events. This eliminates polling, reduces manual status checks, and keeps your CRM CRMCustomer Relationship Management software. The system of record for contacts, deals, and client communication. Examples: HubSpot, Salesforce, Pipedrive. and practice management system synchronized automatically.
This guide walks you through the complete technical setup, from generating webhook credentials to handling signature verification in production code.
What You Need Before Starting
Adobe Sign Enterprise or Business Account
You need admin access to create webhooks. Personal accounts don't support webhook configuration. Log in at https://secure.na1.adobesign.com/public/admin and verify you can access Account > 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 Secret Generator
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 structure works with Python (Flask/FastAPI), Ruby (Rails/Sinatra), or any HTTP server framework.
Step 1: Create the Webhook in Adobe Sign
Log in to Adobe Sign and navigate to Account > Webhooks.
Click "Create a webhook" in the top right.
Webhook name: Use a descriptive identifier like
production-client-onboardingorstaging-engagement-letters.Webhook URL: Enter your public endpoint. Format:
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.
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.
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.
Step 2: Configure Webhook Authentication
Adobe Sign uses HMAC-SHA256 signatures to verify webhook authenticity.
Navigate to Account > Webhooks, click your webhook name, then scroll to "Webhook signature key".
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 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 route. You need the raw body buffer to verify the signature correctly.
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 APIApplication Programming Interface. The connection point that lets two pieces of software exchange data. How n8n talks to your CRM. calls inside webhook handlers. Adobe Sign expects responses within 10 seconds.
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 webhook URL to https://abc123.ngrok.io/webhooks/adobe-sign.
Send a Test Webhook:
In Adobe Sign, go to your webhook settings and click "Test webhook". This sends a sample AGREEMENT_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 webhook received
AGREEMENT_ACTION_COMPLETEDandAGREEMENT_WORKFLOW_COMPLETEDevents. - Check that your CRM or database updated correctly.
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 times out: Adobe Sign retries failed webhooks up to 10 times with exponential backoff. If your endpoint is slow, process events asynchronously using a job queue.
Missing events: Check your webhook event selections in Adobe Sign. Some events (like AGREEMENT_MODIFIED) aren't enabled by default.
Step 5: Handle Webhook Retries
Adobe Sign retries failed webhooks (non-200 responses) with this schedule:
- 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 Health
Adobe Sign provides webhook delivery logs at Account > Webhooks > [Your Webhook] > Activity.
Check this weekly for:
- Failed deliveries (4xx/5xx responses)
- Slow response times (>5 seconds)
- Disabled webhooks (Adobe Sign auto-disables after 100 consecutive failures)
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:
- [ ] Webhook secret stored in environment variables, not code
- [ ] 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 webhook delivery failures
- [ ] Tested with all selected event types
- [ ] Documented webhook URL and event mappings for your team
Adobe Sign webhooks eliminate the need for polling and provide instant updates when clients interact with your agreements. Proper implementation ensures your systems stay synchronized without manual intervention.
Related Resources
E-Signature Webhook Setup Guide (DocuSign)
Configuring DocuSign completion webhook to trigger n8n.
E-Signature Webhook Setup Guide (PandaDoc)
Same for PandaDoc.
Onboarding Process Mapping Worksheet
Step-by-step template to map current onboarding steps, owners, and automation candidates.
The full system, end to end.
Looking to build your AI workforce? Get the comprehensive guide for professional services - the 12 plays, the frameworks, and the field-tested playbooks.
Buy on Amazon
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.
Get the Book
Need help turning this guide into reality?
Revenue Institute builds and implements the AI workforce for professional services firms.
Work with Revenue Institute