9.0 KiB
CLM Doc Gen Integration Guide
Overview
This guide explains how to integrate Salesforce with DocuSign CLM to generate Appraiser Review Letters dynamically from Appraiser Case records.
Architecture
Components
- Salesforce Objects: Appraiser_Case__c and Appraiser_Case_Deficiency__c
- Apex Payload Builder:
AppraiserCasePayloadBuilder- transforms SOQL data into CLM merge format - HTTP Callout:
CLMDocGenCallout- invokes DocuSign CLM API - CLM Template: DocuSign CLM-hosted template with merge fields and repeat blocks
- Flow or Apex Trigger: Orchestrates when/how document generation happens
Data Flow
Appraiser Case (UI/API)
↓
Salesforce Apex/Flow
↓
AppraiserCasePayloadBuilder.buildPayload() [builds merge data]
↓
CLMDocGenCallout.generateDocument() [sends to CLM API]
↓
DocuSign CLM
↓
Generated PDF + Envelope
↓
Recipient Email
Setup Steps
1. Configure Named Credentials (Salesforce)
Goal: Store CLM API endpoint and authentication credentials securely.
- Go to Setup → Named Credentials → New
- Configure:
- Label:
CLMNamedCred - URL:
https://[your-clm-instance].docusign.com - Authentication Protocol:
OAuth 2.0 - Client ID: (from DocuSign CLM admin console)
- Client Secret: (from DocuSign CLM admin console)
- Scope: (typically
signature) - Token Endpoint:
https://[your-clm-instance].docusign.com/oauth/token
- Label:
- Save
Alternative: For testing, use a custom Named Credential with API Key auth if available from your CLM admin.
2. Configure Remote Site Settings (Salesforce)
Goal: Whitelist CLM domain for HTTP callouts.
- Go to Setup → Remote Site Settings → New
- Configure:
- Remote Site Name:
DocuSignCLM - Remote Site URL:
https://[your-clm-instance].docusign.com - Disable Protocol Security: (unchecked for production)
- Remote Site Name:
- Save
3. Get CLM Template ID
Goal: Identify which CLM template to use for Appraiser Review Letters.
- In DocuSign CLM admin console, navigate to Templates
- Find or create the Appraiser Review Letter template
- Note the Template ID (usually a UUID or numeric string)
- Verify the template expects these merge fields:
AppraiserCaseNumber(Text)AppraiserFieldReviewDate(Date)PropertyAddress(Text)DeficiencyList[](Array/Lines table with deficiencyNumber, description, resolution)
Usage Patterns
Pattern 1: Apex Trigger (Automatic)
Scenario: Generate letter automatically when Appraiser Case status reaches "Ready for Review"
// In a trigger on Appraiser_Case__c AFTER UPDATE
if (oldMap.get(record.Id).Status__c != 'Ready for Review' &&
record.Status__c == 'Ready for Review') {
CLMDocGenCallout.CLMDocGenResponse response = CLMDocGenCallout.generateDocument(
record.Id,
'TEMPLATE_ID_FROM_CLM', // e.g., '123456'
record.Reviewer_Email__c
);
if (!response.success) {
// Log error or send notification
System.debug('CLM Doc Gen failed: ' + response.message);
}
}
Pattern 2: Flow (UI-Driven)
Scenario: User clicks button to generate letter on-demand
- Create a Record-Triggered Flow on Appraiser_Case__c
- Add an Action step:
- Action Type: "Apex Action"
- Apex Class: Choose custom Apex action that wraps
CLMDocGenCallout.generateDocument()
- Pass:
- recordId (the Appraiser Case)
- templateId (hardcoded or allow user to select)
- recipientEmail (from record or user input)
- On success: Show toast, store document URL on record
- On error: Show error message
Pattern 3: REST API (External System)
Scenario: External system calls Salesforce to generate letter
@RestResource(urlMapping='/appraiser-case-generate-letter')
global class AppraiserCaseDocGenRest {
@HttpPost
global static void generateLetter(String caseId, String templateId, String recipientEmail) {
CLMDocGenCallout.CLMDocGenResponse response = CLMDocGenCallout.generateDocument(
caseId, templateId, recipientEmail
);
// Return response
RestContext.response.statusCode = response.success ? 200 : 400;
RestContext.response.responseBody = Blob.valueOf(JSON.serialize(response));
}
}
Payload Structure
Input
{
"AppraiserCaseNumber": "AC-00001",
"AppraiserFieldReviewDate": "2026-04-02",
"PropertyAddress": "123 Main St, Denver, CO 80202",
"DeficiencyList": [
{
"deficiencyNumber": 1,
"description": "Missing comparable sale adjustment detail.",
"resolution": "Added adjustment rationale and supporting calculations."
},
{
"deficiencyNumber": 2,
"description": "Neighborhood trend explanation insufficient.",
"resolution": "Expanded market trend narrative with MLS evidence."
},
{
"deficiencyNumber": 3,
"description": "Photo date stamps were not included.",
"resolution": "Re-uploaded photos with date metadata and captions."
}
]
}
CLM API Request (what CLMDocGenCallout sends)
{
"templateId": "TEMPLATE_ID_FROM_CLM",
"mergeData": { ...payload above... },
"delivery": {
"recipientEmail": "reviewer@example.com",
"documentName": "AppraiserReviewLetter_1743724800000"
},
"metadata": {
"salesforceRecordId": "a0wKW000007OIiCYAW",
"generatedAt": "2026-04-02T05:27:44Z"
}
}
CLM API Response (on success)
{
"success": true,
"documentUrl": "https://clm-instance.docusign.com/documents/ABC123XYZ",
"documentId": "DOC-001",
"message": "Document generated successfully"
}
CLM Template Design
Template Merge Tags (Handlebars syntax)
Flat fields:
<p>Case Number: {{AppraiserCaseNumber}}</p>
<p>Review Date: {{AppraiserFieldReviewDate}}</p>
<p>Property: {{PropertyAddress}}</p>
Deficiency repeat block:
<table>
<tr>
<th>Deficiency #</th>
<th>Description</th>
<th>Resolution</th>
</tr>
{{#each DeficiencyList}}
<tr>
<td>{{deficiencyNumber}}</td>
<td>{{description}}</td>
<td>{{resolution}}</td>
</tr>
{{/each}}
</table>
Conditional (if no deficiencies):
{{#if DeficiencyList.length}}
<!-- Deficiency table -->
{{else}}
<p>No deficiencies found.</p>
{{/if}}
Testing
Unit Test (Apex)
sf apex run test --test-level RunLocalTests --target-org appraiser-dev
Expected: AppraiserCasePayloadBuilderTest passes all assertions.
Integration Test (Manual)
- In Salesforce, create an Appraiser Case with 2-3 sample deficiencies
- Run (in Apex Execute):
String caseId = 'a0wKW000007OIiCYAW'; Map<String, Object> payload = AppraiserCasePayloadBuilder.buildPayload(caseId); System.debug(JSON.serialize(payload)); - Copy payload output
- Verify all fields and DeficiencyList array are populated
CLM Integration Test
- Set up Named Credentials and Remote Site Settings (see Setup section)
- Configure CLM template ID in CLMDocGenCallout
- Run (in Apex Execute):
String caseId = 'a0wKW000007OIiCYAW'; String templateId = 'TEMPLATE_123'; String recipientEmail = 'test@example.com'; CLMDocGenCallout.CLMDocGenResponse response = CLMDocGenCallout.generateDocument( caseId, templateId, recipientEmail ); System.debug('Success: ' + response.success); System.debug('Message: ' + response.message); System.debug('Document URL: ' + response.documentUrl); - Monitor CLM instance for outbound document delivery
Troubleshooting
"Document generated successfully" but no email received
- Check recipient email in CLM settings (delivery rules may have delay)
- Verify Email-to-Sign integration is enabled in CLM
- Check CLM audit log for delivery status
HTTP 401 Unauthorized (Named Credentials)
- Verify OAuth token is valid in Named Credentials
- Refresh token or re-authorize
- Check OAuth scope matches CLM permissions
"Appraiser Case not found" error
- Verify record ID is correct
- Ensure Appraiser_Case__c object permissions are granted to running user
Empty DeficiencyList in generated document
- Check that related Appraiser_Case_Deficiency__c records exist
- Verify CLM template correctly references {{DeficiencyList}}
- Test payload in Apex to confirm array is populated
Performance Notes
AppraiserCasePayloadBuilder.buildPayload()runs one query (with related records in subquery)CLMDocGenCallout.generateDocument()performs one HTTP callout (blocks execution ~1-5 seconds)- For bulk operations, consider queueable jobs or batch class to manage API rate limits
Next Steps
- ✅ Deploy Apex classes to org (included in manifest)
- Configure Named Credentials with CLM OAuth/API credentials
- Add CLM Template ID to CLMDocGenCallout (configurable constant or custom setting)
- Build a Flow or Trigger to invoke CLMDocGenCallout
- Test end-to-end with sample Appraiser Case records
Last updated: 2026-04-02 Updated to include setup instructions and integration patterns.