324 lines
9.0 KiB
Markdown
324 lines
9.0 KiB
Markdown
# 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
|
|
1. **Salesforce Objects**: Appraiser_Case__c and Appraiser_Case_Deficiency__c
|
|
2. **Apex Payload Builder**: `AppraiserCasePayloadBuilder` - transforms SOQL data into CLM merge format
|
|
3. **HTTP Callout**: `CLMDocGenCallout` - invokes DocuSign CLM API
|
|
4. **CLM Template**: DocuSign CLM-hosted template with merge fields and repeat blocks
|
|
5. **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.
|
|
|
|
1. Go to Setup → Named Credentials → New
|
|
2. 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`
|
|
3. 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.
|
|
|
|
1. Go to Setup → Remote Site Settings → New
|
|
2. Configure:
|
|
- Remote Site Name: `DocuSignCLM`
|
|
- Remote Site URL: `https://[your-clm-instance].docusign.com`
|
|
- Disable Protocol Security: (unchecked for production)
|
|
3. Save
|
|
|
|
### 3. Get CLM Template ID
|
|
|
|
**Goal**: Identify which CLM template to use for Appraiser Review Letters.
|
|
|
|
1. In DocuSign CLM admin console, navigate to Templates
|
|
2. Find or create the Appraiser Review Letter template
|
|
3. Note the Template ID (usually a UUID or numeric string)
|
|
4. 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"
|
|
|
|
```apex
|
|
// 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
|
|
|
|
1. Create a Record-Triggered Flow on Appraiser_Case__c
|
|
2. Add an Action step:
|
|
- Action Type: "Apex Action"
|
|
- Apex Class: Choose custom Apex action that wraps `CLMDocGenCallout.generateDocument()`
|
|
3. Pass:
|
|
- recordId (the Appraiser Case)
|
|
- templateId (hardcoded or allow user to select)
|
|
- recipientEmail (from record or user input)
|
|
4. On success: Show toast, store document URL on record
|
|
5. On error: Show error message
|
|
|
|
### Pattern 3: REST API (External System)
|
|
|
|
**Scenario**: External system calls Salesforce to generate letter
|
|
|
|
```apex
|
|
@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
|
|
```json
|
|
{
|
|
"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)
|
|
```json
|
|
{
|
|
"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)
|
|
```json
|
|
{
|
|
"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**:
|
|
```handlebars
|
|
<p>Case Number: {{AppraiserCaseNumber}}</p>
|
|
<p>Review Date: {{AppraiserFieldReviewDate}}</p>
|
|
<p>Property: {{PropertyAddress}}</p>
|
|
```
|
|
|
|
**Deficiency repeat block**:
|
|
```handlebars
|
|
<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)**:
|
|
```handlebars
|
|
{{#if DeficiencyList.length}}
|
|
<!-- Deficiency table -->
|
|
{{else}}
|
|
<p>No deficiencies found.</p>
|
|
{{/if}}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Unit Test (Apex)
|
|
```bash
|
|
sf apex run test --test-level RunLocalTests --target-org appraiser-dev
|
|
```
|
|
|
|
Expected: AppraiserCasePayloadBuilderTest passes all assertions.
|
|
|
|
### Integration Test (Manual)
|
|
|
|
1. In Salesforce, create an Appraiser Case with 2-3 sample deficiencies
|
|
2. Run (in Apex Execute):
|
|
```apex
|
|
String caseId = 'a0wKW000007OIiCYAW';
|
|
Map<String, Object> payload = AppraiserCasePayloadBuilder.buildPayload(caseId);
|
|
System.debug(JSON.serialize(payload));
|
|
```
|
|
3. Copy payload output
|
|
4. Verify all fields and DeficiencyList array are populated
|
|
|
|
### CLM Integration Test
|
|
|
|
1. Set up Named Credentials and Remote Site Settings (see Setup section)
|
|
2. Configure CLM template ID in CLMDocGenCallout
|
|
3. Run (in Apex Execute):
|
|
```apex
|
|
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);
|
|
```
|
|
4. 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
|
|
|
|
1. ✅ Deploy Apex classes to org (included in manifest)
|
|
2. Configure Named Credentials with CLM OAuth/API credentials
|
|
3. Add CLM Template ID to CLMDocGenCallout (configurable constant or custom setting)
|
|
4. Build a Flow or Trigger to invoke CLMDocGenCallout
|
|
5. Test end-to-end with sample Appraiser Case records
|
|
|
|
---
|
|
|
|
_Last updated: 2026-04-02_
|
|
_Updated to include setup instructions and integration patterns._
|