320 lines
11 KiB
Markdown
320 lines
11 KiB
Markdown
# CLM Doc Gen Integration Guide
|
|
|
|
> Status note (2026-04-07): the repo is now standardized on the XML merge path built on `AppraiserCasePayloadBuilder` and `CLMDocGenCallout`, using `Appraiser_Case_Deficiency__c` as the canonical child object. See `CURRENT_STATUS.md` before using this guide as the sole source of truth.
|
|
|
|
## 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: Queueable Apex (Automatic/Trigger-Driven)
|
|
|
|
**Scenario**: Generate letter automatically from a trigger or process (callouts cannot be made synchronously from triggers — use a queueable)
|
|
|
|
```apex
|
|
public class AppraiserCaseDocGenJob implements Queueable, Database.AllowsCallouts {
|
|
private Id caseId;
|
|
private String accountCode;
|
|
private String templateDocHref;
|
|
private String destinationFolderHref;
|
|
private String destinationDocName;
|
|
|
|
public AppraiserCaseDocGenJob(Id caseId, String accountCode,
|
|
String templateDocHref, String destinationFolderHref, String destinationDocName) {
|
|
this.caseId = caseId;
|
|
this.accountCode = accountCode;
|
|
this.templateDocHref = templateDocHref;
|
|
this.destinationFolderHref = destinationFolderHref;
|
|
this.destinationDocName = destinationDocName;
|
|
}
|
|
|
|
public void execute(QueueableContext ctx) {
|
|
CLMDocGenCallout.CLMDocGenResponse response = CLMAdminService.generateDocument(
|
|
caseId, templateDocHref, destinationFolderHref, destinationDocName, accountCode
|
|
);
|
|
if (!response.success) {
|
|
System.debug('CLM Doc Gen failed: ' + response.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enqueue from a trigger:
|
|
System.enqueueJob(new AppraiserCaseDocGenJob(
|
|
record.Id,
|
|
'DTC_CLM_Demo',
|
|
letterSettings.defaultTemplateDocumentHref,
|
|
letterSettings.destinationRootFolderHref,
|
|
'Review_' + record.Name + '.docx'
|
|
));
|
|
```
|
|
|
|
### Pattern 2: LWC Quick Action (UI-Driven) ✅ Implemented
|
|
|
|
The `clmDocGenWorkbench` component, launched from the **Generate Review Letter** quick action, is the current implemented UI path. It calls `CLMAdminService` to:
|
|
- Browse available accounts and letter types
|
|
- Browse CLM template and destination folders
|
|
- Submit a merge task via `generateDocument()`
|
|
- Poll task status via `getTaskStatus()`
|
|
- Attach the generated file to the case via `attachGeneratedDocumentToCase()`
|
|
|
|
No additional configuration is needed beyond deploying the metadata and setting up Named Credentials.
|
|
|
|
### 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 accountCode,
|
|
String templateDocHref,
|
|
String destinationFolderHref,
|
|
String destinationDocName
|
|
) {
|
|
CLMDocGenCallout.CLMDocGenResponse response = CLMAdminService.generateDocument(
|
|
(Id) caseId, templateDocHref, destinationFolderHref, destinationDocName, accountCode
|
|
);
|
|
RestContext.response.statusCode = response.success ? 200 : 400;
|
|
RestContext.response.responseBody = Blob.valueOf(JSON.serialize(response));
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Payload Structure
|
|
|
|
### AppraiserCasePayloadBuilder output (intermediate JSON)
|
|
```json
|
|
{
|
|
"AppraiserCaseNumber": "AC-000001",
|
|
"AppraiserFieldReviewDate": "Apr 02, 2026",
|
|
"LetterSentDate": "Apr 09, 2026",
|
|
"FHACaseNumber": "123-4567890",
|
|
"AppraiserName": "Jamie Appraiser",
|
|
"AppraiserSalutation": "Ms.",
|
|
"AppraiserLastName": "Appraiser",
|
|
"AppraiserEmail": "jamie@example.com",
|
|
"AppraiserAddress": "245 Lexington Ave, New York, NY 10016, USA",
|
|
"PropertyAddress": "123 Main St, Denver, CO 80202, USA",
|
|
"DeficiencyList": [
|
|
{ "deficiencyNumber": 1, "description": "...", "resolution": "...", "reference": "VC-1" }
|
|
]
|
|
}
|
|
```
|
|
|
|
### CLM API Request (what CLMDocGenCallout sends to `/documentxmlmergetasks`)
|
|
```json
|
|
{
|
|
"TemplateDocument": { "Href": "https://.../documents/<template-guid>" },
|
|
"DataXML": "<TemplateFieldData><AppraiserCaseNumber>AC-000001</AppraiserCaseNumber>...<DeficiencyList><Deficiency><Number>1</Number>...</Deficiency></DeficiencyList></TemplateFieldData>",
|
|
"DestinationDocumentName": "Review_AC-000001.docx",
|
|
"DestinationFolder": { "Href": "https://.../folders/<folder-guid>" }
|
|
}
|
|
```
|
|
|
|
### CLM API Response (on success)
|
|
```json
|
|
{
|
|
"Href": "https://apiuatna11.springcm.com/v2/<account-id>/documentxmlmergetasks/<task-guid>",
|
|
"Status": "Queued",
|
|
"Result": { "Href": "https://apiuatna11.springcm.com/v2/<account-id>/documents/<doc-guid>" }
|
|
}
|
|
```
|
|
|
|
The response is persisted to `Appraiser_Case__c` tracking fields by `CLMAdminService.persistDocGenResult()`.
|
|
|
|
---
|
|
|
|
## CLM Template Design
|
|
|
|
The implementation uses the DocuSign CLM **XML merge** (`documentxmlmergetasks`) API, not a Handlebars/template-key approach. Template field tags in the `.docx` file use CLM's Word merge syntax (typically `«FieldName»` merge fields or CLM repeat-block markers). Refer to your CLM tenant documentation for the exact syntax.
|
|
|
|
**Flat field example** (Word merge field in the .docx):
|
|
```
|
|
«AppraiserCaseNumber» «AppraiserFieldReviewDate»
|
|
«PropertyAddress»
|
|
```
|
|
|
|
**Deficiency repeat block** — the XML structure sent is:
|
|
```xml
|
|
<DeficiencyList>
|
|
<Deficiency>
|
|
<Number>1</Number>
|
|
<Description>Missing comparable sale adjustment detail.</Description>
|
|
<Resolution>Added adjustment rationale and supporting calculations.</Resolution>
|
|
<Reference>VC-1</Reference>
|
|
</Deficiency>
|
|
</DeficiencyList>
|
|
<DeficiencyCount>1</DeficiencyCount>
|
|
```
|
|
|
|
Configure a repeat region in your CLM template over `DeficiencyList/Deficiency`, binding `Number`, `Description`, `Resolution`, and `Reference` within the loop.
|
|
|
|
---
|
|
|
|
## 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._
|