Play 5 Complete Implementation Guide
Full walkthrough: e-signature webhook, new/existing client paths, welcome email, folder creation.
Play 5 Complete Implementation Guide
Client onboarding automation separates firms that scale from firms that stall. This guide walks you through building a production-grade onboarding system that triggers the moment a client signs your engagement letter.
You'll configure e-signature webhooks webhooksClick to read the full definition in our AI & Automation Glossary., route new versus returning clients, automate welcome sequences, and provision folder structures. No theory. Just the exact steps to deploy this week.
Prerequisites
Before you start, confirm you have:
- Admin access to your e-signature platform (DocuSign, PandaDoc, or HelloSign)
- Webhook receiver endpoint (Make.com, Zapier, or custom server)
- File storage admin rights (Google Drive, SharePoint, or Dropbox Business)
- SMTP credentials or email automation tool (SendGrid, Mailgun, or Gmail API APIApplication Programming Interface. The connection point that lets two pieces of software exchange data. How n8n talks to your CRM.)
E-Signature Webhook Configuration
DocuSign Setup
Step 1: Access Connect Settings
Log into DocuSign as an admin. Navigate to Settings > Connect > Add Configuration. Select "Custom" as the configuration type.
Step 2: Configure the Webhook URL
Enter your webhook receiver URL. For Make.com, this looks like: https://hook.us1.make.com/abc123xyz. For Zapier: https://hooks.zapier.com/hooks/catch/123456/abc123/.
Step 3: Select Trigger Events
Enable only these events:
- Envelope Sent
- Envelope Completed
- Recipient Completed
Disable all others to reduce noise.
Step 4: Set Delivery Mode
Choose "Aggregate Messages" with a 5-minute interval. This batches multiple events and prevents webhook flooding during high-volume periods.
Step 5: Configure Authentication
Under "Basic Authentication", generate a username and password. Store these in your webhook receiver as environment variables. DocuSign will send these credentials in the Authorization header of every webhook request.
Step 6: Enable Logging
Turn on "Log Sent Messages" for 30 days. This creates an audit trail you can reference when debugging failed deliveries.
Step 7: Test the Connection
Click "Test Connection". DocuSign sends a sample payload to your endpoint. Verify your receiver logs the incoming request with a 200 status code.
PandaDoc Setup
Step 1: Navigate to Webhooks
Go to Settings > Developers > Webhooks > Create Webhook.
Step 2: Enter Endpoint URL
Paste your webhook receiver URL. PandaDoc supports HTTPS only.
Step 3: Select Events
Enable:
- document.completed
- document.sent
Step 4: Add Shared Secret
PandaDoc generates a shared secret automatically. Copy this value. Your webhook receiver must validate the X-PandaDoc-Signature header against this secret using HMAC-SHA256.
Validation example (Python):
import hmac
import hashlib
def validate_pandadoc_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Step 5: Test Delivery
Send a test document to yourself. Complete it. Check your webhook receiver logs for the incoming POST request.
HelloSign Setup
Step 1: Access API Settings
Navigate to Settings > API > Callbacks.
Step 2: Add Callback URL
Enter your webhook endpoint. HelloSign calls this a "callback" instead of a webhook.
Step 3: Generate API Key
Under "API Key", click "Reveal" and copy your key. Store this securely. HelloSign includes this in the event.event_hash field for validation.
Step 4: Enable Events
Select:
- signature_request_sent
- signature_request_signed
- signature_request_all_signed
Step 5: Verify Event Hash
HelloSign sends an event_hash with each callback. Validate it by computing: HMAC-SHA256(event_time + event_type, api_key).
Validation example (Node.js):
const crypto = require('crypto');
function validateHelloSign(eventTime, eventType, eventHash, apiKey) {
const data = eventTime + eventType;
const expected = crypto
.createHmac('sha256', apiKey)
.update(data)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(eventHash)
);
}
New Client Workflow
Step 1: Receive and Parse Webhook
Your webhook receiver gets a POST request when a client completes signing. Extract these fields from the payload:
DocuSign:
envelopeIdrecipients[0].emailrecipients[0].namestatus(should equal "completed")
PandaDoc:
data.iddata.recipients[0].emaildata.recipients[0].first_name+data.recipients[0].last_namedata.status(should equal "document.completed")
HelloSign:
signature_request.signature_request_idsignature_request.signatures[0].signer_email_addresssignature_request.signatures[0].signer_nameevent.event_type(should equal "signature_request_all_signed")
Step 2: Check for Existing Client
Query your CRM before creating a new record. Use email as the unique identifier.
Clio API example:
curl -X GET "https://app.clio.com/api/v4/contacts.json?query=john.doe@example.com" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Salesforce SOQL example:
SELECT Id, Name, Email FROM Contact
WHERE Email = 'john.doe@example.com'
LIMIT 1
If the query returns a record, route to the existing client workflow. If empty, continue to Step 3.
Step 3: Create Client Record
Insert a new contact or matter in your CRM.
Clio API example:
curl -X POST "https://app.clio.com/api/v4/contacts.json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "John Doe",
"email_addresses": [{"name": "Work", "address": "john.doe@example.com"}],
"type": "Company"
}
}'
Practice Panther API example:
curl -X POST "https://api.practicepanther.com/v1/contacts" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "John Doe",
"email": "john.doe@example.com",
"contact_type": "client"
}'
Store the returned client ID. You'll need it for folder creation and future API calls.
Step 4: Send Welcome Email
Use a transactional email service for reliable delivery and tracking.
SendGrid API example:
curl -X POST "https://api.sendgrid.com/v3/mail/send" \
-H "Authorization: Bearer YOUR_SENDGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"personalizations": [{
"to": [{"email": "john.doe@example.com", "name": "John Doe"}],
"dynamic_template_data": {
"client_name": "John",
"firm_name": "Smith & Associates",
"portal_url": "https://portal.smithlaw.com/login"
}
}],
"from": {"email": "welcome@smithlaw.com", "name": "Smith & Associates"},
"template_id": "d-abc123xyz"
}'
Email Template Structure:
Subject: Your engagement with [FIRM_NAME] starts now
Body:
Hi [CLIENT_FIRST_NAME],
Your engagement letter is signed. Here's what happens next:
1. Your dedicated team lead will email you within 24 hours to schedule a kickoff call
2. You'll receive login credentials for our client portal at [PORTAL_URL]
3. We'll send a detailed project timeline by end of week
Access your client portal: [PORTAL_URL]
Questions? Reply to this email or call [PHONE_NUMBER].
[PARTNER_NAME]
[FIRM_NAME]
Step 5: Create Folder Structure
Provision a standardized folder hierarchy in your file storage system.
Google Drive API example (Python):
from googleapiclient.discovery import build
def create_client_folders(client_name, parent_folder_id):
service = build('drive', 'v3', credentials=creds)
# Create root client folder
root_folder = service.files().create(body={
'name': client_name,
'mimeType': 'application/vnd.google-apps.folder',
'parents': [parent_folder_id]
}).execute()
# Create subfolders
subfolders = [
'01_Engagement_Letters',
'02_Work_Product',
'03_Client_Communications',
'04_Invoices_and_Billing',
'05_Source_Documents'
]
for folder_name in subfolders:
service.files().create(body={
'name': folder_name,
'mimeType': 'application/vnd.google-apps.folder',
'parents': [root_folder['id']]
}).execute()
return root_folder['id']
SharePoint API example (PowerShell):
$clientName = "John Doe"
$siteUrl = "https://yourfirm.sharepoint.com/sites/ClientMatters"
Connect-PnPOnline -Url $siteUrl -Interactive
$rootFolder = Add-PnPFolder -Name $clientName -Folder "Shared Documents"
$subfolders = @(
"01_Engagement_Letters",
"02_Work_Product",
"03_Client_Communications",
"04_Invoices_and_Billing",
"05_Source_Documents"
)
foreach ($folder in $subfolders) {
Add-PnPFolder -Name $folder -Folder "$($rootFolder.ServerRelativeUrl)"
}
Step 6: Move Signed Document
Download the completed engagement letter from your e-signature platform and upload it to the client's folder.
DocuSign document download:
curl -X GET "https://demo.docusign.net/restapi/v2.1/accounts/ACCOUNT_ID/envelopes/ENVELOPE_ID/documents/combined" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-o "engagement_letter.pdf"
Upload to Google Drive:
file_metadata = {
'name': 'Engagement_Letter_Signed.pdf',
'parents': [engagement_letters_folder_id]
}
media = MediaFileUpload('engagement_letter.pdf', mimetype='application/pdf')
service.files().create(body=file_metadata, media_body=media).execute()
Existing Client Workflow
Step 1: Receive Webhook
Parse the incoming webhook payload exactly as in the new client workflow.
Step 2: Retrieve Client Record
Query your CRM using the email address from the webhook.
HubSpot API example:
curl -X GET "https://api.hubapi.com/contacts/v1/contact/email/john.doe@example.com/profile" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
If no record exists, fall back to the new client workflow.
Step 3: Update Client Status
Mark the client as "Active" or "Re-engaged" in your CRM.
Salesforce API example:
curl -X PATCH "https://yourinstance.salesforce.com/services/data/v57.0/sobjects/Contact/CONTACT_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Status__c": "Active",
"Last_Engagement_Date__c": "2024-01-15"
}'
Step 4: Send Welcome Back Email
Use a different template that acknowledges the prior relationship.
Subject: Welcome back to [FIRM_NAME]
Body:
Hi [CLIENT_FIRST_NAME],
Great to work with you again. Your new engagement letter is signed and filed.
What's changed since we last worked together:
- New client portal with real-time project tracking: [PORTAL_URL]
- Expanded service offerings in [PRACTICE_AREA]
- Faster turnaround times (average 3 business days)
Your team lead will reach out within 24 hours to discuss your current needs.
Access your portal: [PORTAL_URL]
[PARTNER_NAME]
[FIRM_NAME]
Step 5: Create New Matter Folder
Add a new subfolder under the existing client's root folder. Use a naming convention that includes the matter number and date.
Folder name format: [MATTER_NUMBER]_[MATTER_NAME]_[YYYY-MM]
Example: 2024-003_Estate_Planning_2024-01
Google Drive example:
matter_folder = service.files().create(body={
'name': '2024-003_Estate_Planning_2024-01',
'mimeType': 'application/vnd.google-apps.folder',
'parents': [existing_client_folder_id]
}).execute()
# Create standard subfolders within the matter folder
matter_subfolders = [
'01_Engagement_Letters',
'02_Work_Product',
'03_Correspondence',
'04_Source_Documents'
]
for folder_name in matter_subfolders:
service.files().create(body={
'name': folder_name,
'mimeType': 'application/vnd.google-apps.folder',
'parents': [matter_folder['id']]
}).execute()
Error Handling and Monitoring
Webhook Delivery Failures
E-signature platforms retry failed webhooks 3-5 times with exponential backoff. If your endpoint is down, you'll miss events.
Solution: Implement a dead letter queue. When your webhook receiver fails to process an event, write it to a separate queue for manual review.
Make.com: Add an error handler route that writes failed scenarios to a Google Sheet.
Zapier: Enable "Error Notifications" in Zap settings. Failed runs email you with the full payload.
Custom server: Use a message queue like AWS SQS or RabbitMQ.
Duplicate Client Prevention
If your CRM query fails but the client exists, you'll create a duplicate record.
Solution: Add a secondary check using phone number or company name. If email query returns empty but phone query returns a match, route to existing client workflow.
Folder Creation Conflicts
If two webhooks fire simultaneously for the same client, you might create duplicate folders.
Solution: Implement idempotency. Before creating a folder, search for existing folders with the same name. If found, use that folder ID instead of creating a new one.
Google Drive search:
query = f"name='{client_name}' and mimeType='application/vnd.google-apps.folder' and '{parent_folder_id}' in parents"
results = service.files().list(q=query, fields='files(id, name)').execute()
if results.get('files'):
return results['files'][0]['id']
Email Deliverability
Welcome emails might land in spam if your domain lacks proper authentication.
Solution: Configure SPF, DKIM, and DMARC records for your sending domain. Use a dedicated subdomain like mail.yourfirm.com for transactional emails.
SPF record example:
v=spf1 include:sendgrid.net ~all
DKIM: Your email provider generates this. Add the provided TXT record to your DNS.
DMARC record example:
v=DMARC1; p=quarantine; rua=mailto:dmarc@yourfirm.com
Related Resources
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