salesforce-composite-envelo.../composite-envelope-builder/docs/design.md

28 KiB
Raw Permalink Blame History

Design Document

Project: Salesforce Composite Envelope Builder
Version: 1.1
Date: February 23, 2026 (updated March 11, 2026)
Author: Paul Huliganga


1. Architecture Overview

1.1 System Context

┌──────────────────┐
│  Salesforce User │
└────────┬─────────┘
         │
         ▼
┌─────────────────────────────────────────┐
│     Salesforce Platform                  │
│  ┌──────────────┐    ┌───────────────┐  │
│  │ Screen Flow  │───▶│  Apex Class   │  │
│  │ (Template    │    │  (Composite   │  │
│  │  Selection)  │    │   Builder)    │  │
│  └──────────────┘    └───────┬───────┘  │
│                              │           │
└──────────────────────────────┼───────────┘
                               │
                               ▼ HTTPS/REST API
                    ┌──────────────────────┐
                    │  Docusign REST API   │
                    │  (Composite          │
                    │   Templates)         │
                    └──────────────────────┘

1.2 Component Architecture

┌────────────────────────────────────────────────────────┐
│                  Salesforce Org                         │
│                                                          │
│  ┌─────────────────────────────────────────────────┐   │
│  │  Presentation Layer (Screen Flow)               │   │
│  │  - Language selection                            │   │
│  │  - Template display (checkbox collection)        │   │
│  │  - Success/error messaging                       │   │
│  └──────────────────┬──────────────────────────────┘   │
│                     │                                    │
│  ┌─────────────────▼──────────────────────────────┐   │
│  │  Business Logic Layer (Apex)                    │   │
│  │                                                  │   │
│  │  ┌─────────────────────────────────────────┐   │   │
  │  │  │ DocusignCompositeEnvelopeBuilder       │   │   │
│  │  │ - @InvocableMethod entry point          │   │   │
│  │  │ - Input validation                       │   │   │
│  │  │ - Multi-copy template expansion          │   │   │
│  │  │ - Composite JSON construction            │   │   │
│  │  │ - Envelope ID return                     │   │   │
│  │  └──────────────────┬──────────────────────┘   │   │
│  │                     │                            │   │
│  │  ┌─────────────────▼──────────────────────┐   │   │
│  │  │ DocusignAPIService                     │   │   │
│  │  │ - API authentication                    │   │   │
│  │  │ - HTTP callout construction             │   │   │
│  │  │ - Response parsing                      │   │   │
│  │  │ - Error handling                        │   │   │
│  │  └──────────────────┬──────────────────────┘   │   │
│  │                     │                            │   │
│  │  ┌─────────────────▼──────────────────────┐   │   │
│  │  │ DocusignCredentials                    │   │   │
│  │  │ - Credential retrieval                  │   │   │
│  │  │ - Token management                      │   │   │
│  │  └─────────────────────────────────────────┘   │   │
│  └──────────────────────────────────────────────┘   │
│                                                       │
│  ┌──────────────────────────────────────────────┐   │
│  │  Data Layer                                   │   │
│  │  - Named Credential (Docusign API creds)     │   │
│  │  - Custom Settings (configuration)            │   │
│  └──────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────┘
                     │
                     ▼ HTTPS REST API
          ┌──────────────────────┐
          │  Docusign Platform   │
          └──────────────────────┘

2. Detailed Component Design

2.1 DocusignCompositeEnvelopeBuilder (Main Class)

Purpose: Invocable Apex class that combines multiple Docusign templates into a single envelope

Responsibilities:

  • Receive template IDs from Screen Flow
  • Validate inputs
  • Expand multi-copy templates (e.g. Authorization to Release Information)
  • Construct composite template JSON
  • Delegate API call to service class
  • Return envelope ID to Flow

Methods:

// Flow-invocable entry point
@InvocableMethod(
    label='Send Composite Docusign Envelope' 
    description='Combines multiple templates into one envelope'
)
public static List<Result> sendCompositeEnvelope(List<Request> requests)

// Private helper methods
private static String buildCompositeEnvelopeJSON(
    List<String> templateIds, 
    String recordId,
    String language,
    Map<String, String> customFields
)

private static void validateInputs(Request req)

private static List<String> sortTemplatesAlphabetically(List<String> templateIds)

Inner Classes:

public class Request {
    @InvocableVariable(required=true label='Template IDs')
    public List<String> templateIds;
    
    @InvocableVariable(required=true label='Salesforce Record ID')
    public String recordId;
    
    @InvocableVariable(required=false label='Language')
    public String language; // 'en' or 'es'
    
    @InvocableVariable(required=false label='Email Subject')
    public String emailSubject;
    
    @InvocableVariable(required=false label='Authorization to Release Form Copies')
    public Integer authReleaseFormCopies; // 13; only used when that template is selected
    
    @InvocableVariable(required=false label='Custom Fields')
    public Map<String, String> customFields; // For merge fields
}

public class Result {
    @InvocableVariable(label='Envelope ID')
    public String envelopeId;
    
    @InvocableVariable(label='Success')
    public Boolean success;
    
    @InvocableVariable(label='Error Message')
    public String errorMessage;
}

2.2 DocusignAPIService (Service Class)

Purpose: Handles all Docusign REST API interactions

Responsibilities:

  • Construct HTTP requests
  • Make callouts to Docusign API
  • Parse responses
  • Handle errors and retries
  • Log API interactions

Methods:

public static String createCompositeEnvelope(
    String envelopeJSON, 
    DocusignCredentials creds
)

public static HttpResponse callDocusignAPI(
    String endpoint,
    String method,
    String body,
    Map<String, String> headers
)

public static String parseEnvelopeId(HttpResponse response)

private static void logAPICall(
    HttpRequest req, 
    HttpResponse res, 
    Long durationMs
)

private static void handleAPIError(HttpResponse response)

2.3 DocusignCredentials (Credential Management)

Purpose: Retrieve and manage Docusign API credentials securely

Responsibilities:

  • Fetch credentials from Named Credential or Custom Settings
  • Handle token refresh (JWT/OAuth2)
  • Provide credential object to service layer

Methods:

public static DocusignCredentials getInstance()

public String getAccessToken()

public String getAccountId()

public String getBaseUrl()

private static String refreshAccessToken() // JWT or OAuth2 refresh

public class CredentialException extends Exception {}

Properties:

public String baseUrl { get; private set; }
public String accountId { get; private set; }
public String accessToken { get; private set; }
public DateTime tokenExpiry { get; private set; }

2.4 Multi-Copy Template Feature (v1.1)

Overview

Certain templates may need to be included multiple times in a single envelope (e.g. the Authorization to Release Information form, available in both English and Spanish). Rather than requiring users to build separate envelopes, the flow detects this template and prompts for a copy count before sending.

Configuration

The template is identified by a single constant in DocusignCompositeEnvelopeBuilder.cls:

// ============================================================
// MULTI-COPY TEMPLATE: Update this if the template name changes.
// Both English and Spanish versions share this base name.
// ============================================================
@TestVisible
private static final String MULTI_COPY_TEMPLATE_NAME = 'Authorization to Release Information';

To rename the template, update this one constant. The Apex code uses a LIKE '%<name>%' SOQL query, so the match is case-insensitive and partial — it covers both language variants automatically.

The Flow also has a single Contains check in the Does_Row_Contain_Auth_Release decision node, which must be updated to match.

Flow Changes

The following new elements were added to Docusign_Envelope_Templates_V3:

Element Type Purpose
Scan_For_Auth_Release_Template Loop Iterates all selected rows to look for the target template
Does_Row_Contain_Auth_Release Decision Checks if the current row's Name contains "Authorization to Release Information"
Flag_Auth_Release_Selected Assignment Sets authReleaseTemplateSelected = true when match is found
Is_Auth_Release_Selected Decision After scan loop: routes to copies screen if flag is true, otherwise skips
Authorization_Copies_Screen Screen Shows instruction text + radio buttons (1 copy / 2 copies / 3 copies)
authReleaseFormCopies Variable (Number, default 1) Stores the user's copy-count selection
authReleaseTemplateSelected Variable (Boolean, default false) Flag set during the scan loop
AuthCopies_1/2/3 Choices Radio button options with numeric values 1 / 2 / 3

The authReleaseFormCopies variable is passed to the Apex Invocable Action as a new input parameter.

Updated Flow Path (after row selection)

Check_Row_Selection → Scan_For_Auth_Release_Template (loop)
    │ per row → Does_Row_Contain_Auth_Release
    │               Yes → Flag_Auth_Release_Selected → (continue loop)
    │               No  → (continue loop)
    └ loop ends → Is_Auth_Release_Selected
                      Yes → Authorization_Copies_Screen → Build_Template_ID_Collection
                      No  → Build_Template_ID_Collection (unchanged)

Apex Changes

DocusignEnvelopeRequest.cls — new @InvocableVariable:

@InvocableVariable(
    label='Authorization to Release Form Copies'
    description='Number of times to include the Authorization to Release Information template (1-3).'
    required=false
)
global Integer authReleaseFormCopies;

DocusignCompositeEnvelopeBuilder.cls — multi-copy expansion logic runs before the document list is built:

// Expand multi-copy templates
List<String> expandedTemplateIds = new List<String>(req.templateIds);
Integer copies = (req.authReleaseFormCopies != null && req.authReleaseFormCopies > 1)
    ? Math.min(req.authReleaseFormCopies, 3) : 1;
if (copies > 1) {
    List<String> multiCopyIds = [
        SELECT dfsle__DocuSignId__c FROM dfsle__EnvelopeConfiguration__c
        WHERE dfsle__DocuSignId__c IN :req.templateIds
        AND Name LIKE :('%' + MULTI_COPY_TEMPLATE_NAME + '%')
    ].dfsle__DocuSignId__c;
    for (String id : multiCopyIds) {
        for (Integer i = 1; i < copies; i++) {
            expandedTemplateIds.add(id);
        }
    }
}

Duplicate template IDs are intentionally not deduplicated when multi-copy is in effect. The label builder appends " (Copy 2)" / " (Copy 3)" suffixes to keep document labels distinct within the envelope.


3. Data Flow

3.1 Sequence Diagram: Send Composite Envelope

User          Flow            Apex                Service         Docusign API
 │             │               │                    │                  │
 │ Select      │               │                    │                  │
 │ Templates   │               │                    │                  │
 ├────────────▶│               │                    │                  │
 │             │               │                    │                  │
 │             │ Invoke Apex   │                    │                  │
 │             │ Action        │                    │                  │
 │             ├──────────────▶│                    │                  │
 │             │ (templateIds) │                    │                  │
 │             │               │                    │                  │
 │             │               │ Validate inputs    │                  │
 │             │               ├────────┐           │                  │
 │             │               │        │           │                  │
 │             │               │◀───────┘           │                  │
 │             │               │                    │                  │
 │             │               │ Build JSON         │                  │
 │             │               ├────────┐           │                  │
 │             │               │        │           │                  │
 │             │               │◀───────┘           │                  │
 │             │               │                    │                  │
 │             │               │ Get credentials    │                  │
 │             │               ├───────────────────▶│                  │
 │             │               │                    │                  │
 │             │               │◀───────────────────┤                  │
 │             │               │ (creds)            │                  │
 │             │               │                    │                  │
 │             │               │ Call API           │                  │
 │             │               ├───────────────────▶│                  │
 │             │               │ (JSON, creds)      │                  │
 │             │               │                    │                  │
 │             │               │                    │ POST /envelopes  │
 │             │               │                    ├─────────────────▶│
 │             │               │                    │                  │
 │             │               │                    │ 201 Created      │
 │             │               │                    │ {envelopeId}     │
 │             │               │                    │◀─────────────────┤
 │             │               │                    │                  │
 │             │               │ envelopeId         │                  │
 │             │               │◀───────────────────┤                  │
 │             │               │                    │                  │
 │             │ Result        │                    │                  │
 │             │ (envelopeId)  │                    │                  │
 │             │◀──────────────┤                    │                  │
 │             │               │                    │                  │
 │ Success     │               │                    │                  │
 │ Message     │               │                    │                  │
 │◀────────────┤               │                    │                  │
 │             │               │                    │                  │

3.2 Error Handling Flow

Apex              Service         Docusign API
 │                 │                  │
 │ Call API        │                  │
 ├────────────────▶│                  │
 │                 │ POST /envelopes  │
 │                 ├─────────────────▶│
 │                 │                  │
 │                 │ 400 Bad Request  │
 │                 │ (error JSON)     │
 │                 │◀─────────────────┤
 │                 │                  │
 │                 │ Parse error      │
 │                 ├──────┐           │
 │                 │      │           │
 │                 │◀─────┘           │
 │                 │                  │
 │                 │ Throw exception  │
 │                 │                  │
 │ Catch exception │                  │
 │◀────────────────┤                  │
 │                 │                  │
 │ Log error       │                  │
 ├──────┐          │                  │
 │      │          │                  │
 │◀─────┘          │                  │
 │                 │                  │
 │ Return Result   │                  │
 │ (success=false, │                  │
 │  errorMessage)  │                  │
 │                 │                  │

4. Docusign Composite Template JSON Structure

4.1 Request Format

{
  "status": "sent",
  "emailSubject": "Please review and sign these forms",
  "compositeTemplates": [
    {
      "compositeTemplateId": "1",
      "serverTemplates": [
        {
          "sequence": "1",
          "templateId": "TEMPLATE_ID_FORM_A"
        }
      ]
    },
    {
      "compositeTemplateId": "2",
      "serverTemplates": [
        {
          "sequence": "2",
          "templateId": "TEMPLATE_ID_FORM_B"
        }
      ]
    },
    {
      "compositeTemplateId": "3",
      "serverTemplates": [
        {
          "sequence": "3",
          "templateId": "TEMPLATE_ID_FORM_C"
        }
      ]
    }
  ]
}

4.2 Apex JSON Construction

Approach: Use Map<String, Object> + JSON.serialize() for dynamic construction

List<Object> compositeTemplates = new List<Object>();

Integer sequence = 1;
for (String templateId : sortedTemplateIds) {
    Map<String, Object> template = new Map<String, Object>{
        'compositeTemplateId' => String.valueOf(sequence),
        'serverTemplates' => new List<Object>{
            new Map<String, Object>{
                'sequence' => String.valueOf(sequence),
                'templateId' => templateId
            }
        }
    };
    compositeTemplates.add(template);
    sequence++;
}

Map<String, Object> envelope = new Map<String, Object>{
    'status' => 'sent',
    'emailSubject' => emailSubject,
    'compositeTemplates' => compositeTemplates
};

String envelopeJSON = JSON.serialize(envelope);

5. API Integration Details

5.1 Endpoint

POST https://{baseUrl}/restapi/v2.1/accounts/{accountId}/envelopes

Where:

  • {baseUrl}: e.g., na3.docusign.net (from Named Credential)
  • {accountId}: Docusign account GUID (from Named Credential or Custom Settings)

5.2 Headers

Authorization: Bearer {access_token}
Content-Type: application/json
Accept: application/json

5.3 Authentication

JWT (JSON Web Token) - Preferred:

  • Use RSA key pair stored in Named Credential
  • Generate JWT, exchange for access token
  • Token valid for 1 hour, cache and refresh as needed

OAuth2 Authorization Code - Alternative:

  • User-based authentication
  • Access token + refresh token
  • Requires user interaction for initial authorization

5.4 Response Codes

Code Meaning Action
201 Created Success - parse envelope ID
400 Bad Request Invalid JSON or parameters - log and return error
401 Unauthorized Token expired - refresh and retry
403 Forbidden Insufficient permissions - log and return error
429 Too Many Requests Rate limit - retry after delay
500 Server Error Docusign issue - log and return error

6. Governor Limit Considerations

6.1 Heap Size

  • Limit: 6 MB (synchronous), 12 MB (asynchronous)
  • Mitigation:
    • Serialize JSON incrementally
    • Avoid loading large objects into memory
    • Process template IDs in batches if needed (future enhancement)

6.2 CPU Time

  • Limit: 10,000 ms (synchronous)
  • Mitigation:
    • Minimize loops and complex logic
    • Delegate heavy work to Docusign API

6.3 Callouts

  • Limit: 100 callouts per transaction, 10-second timeout per callout
  • Mitigation:
    • Single API call per envelope
    • Use asynchronous callouts (@future) for bulk operations (future enhancement)

6.4 SOQL Queries

  • Limit: 100 queries per transaction
  • Mitigation:
    • Minimize queries in loop (not applicable for this design)
    • Cache credentials if fetched via SOQL

7. Security Design

7.1 Credential Storage

Option 1: Named Credential (Preferred)

  • Credentials encrypted by Salesforce
  • No code changes needed for credential updates
  • OAuth flows managed by platform

Option 2: Protected Custom Settings

  • API credentials stored encrypted
  • Accessible only via Apex
  • Manual token refresh required

7.2 Access Control

  • Apex class runs in user context (sharing rules enforced)
  • Users must have:
    • Permission to execute Flows
    • Read access to Salesforce record
    • Docusign send permission (managed via Docusign)

7.3 Data Protection

  • No sensitive data logged in debug statements
  • API credentials never exposed in logs or error messages
  • HTTPS for all API communication

8. Testing Strategy

8.1 Unit Tests

Test Coverage Goal: >75% (Salesforce requirement)

Test Classes:

  • DocusignCompositeEnvelopeBuilderTest
  • DocusignAPIServiceTest
  • DocusignCredentialsTest

Test Scenarios:

  1. Success case: 3 templates combined, envelope created
  2. Edge case: 1 template (minimum)
  3. Edge case: 14 templates (maximum)
  4. Error case: Invalid template ID
  5. Error case: API returns 400
  6. Error case: API returns 401 (token expired)
  7. Error case: API timeout
  8. Mock callouts: Use HttpCalloutMock for Docusign API

8.2 Integration Tests

Manual Testing:

  1. Create test Docusign templates in sandbox
  2. Configure Named Credential with sandbox credentials
  3. Execute Screen Flow with various template combinations
  4. Verify envelope creation in Docusign sandbox
  5. Verify documents written back to Salesforce

8.3 Performance Tests

  • Test with maximum templates (14)
  • Measure Apex CPU time
  • Verify heap size usage
  • Test concurrent requests (multiple users)

9. Deployment Architecture

9.1 Deployment Components

Metadata to Deploy:

  1. Apex classes:
    • DocusignCompositeEnvelopeBuilder.cls
    • DocusignAPIService.cls
    • DocusignCredentials.cls
    • Test classes (3 files)
  2. Named Credential configuration
  3. Remote Site Settings (for Docusign API URL)
  4. Permission Sets (optional, for access control)

9.2 Deployment Steps

  1. Sandbox deployment (via VS Code or CLI):

    sf project deploy start --target-org sandbox
    
  2. Run unit tests:

    sf apex run test --wait 5
    
  3. Validate production (without deployment):

    sf project deploy validate --target-org production
    
  4. Deploy to production:

    sf project deploy start --target-org production
    

9.3 Rollback Plan

  • Version control: Git repository tracks all changes
  • Destructive changes: Remove Apex classes if needed
  • Flow modification: Update Flow to call old logic temporarily

10. Monitoring & Maintenance

10.1 Logging

Debug Logs:

  • Log API request/response (sanitized, no credentials)
  • Log execution time
  • Log errors with stack traces

Custom Object (Optional):

  • Store API call history in custom object Docusign_API_Log__c
  • Fields: Timestamp, Envelope ID, Template Count, Status, Error Message

10.2 Alerts

  • Email alerts for repeated API failures
  • Platform Event for real-time monitoring (future enhancement)

10.3 Maintenance Tasks

  • Monitor Docusign API rate limits
  • Review logs for errors
  • Update templates as business needs change
  • Refresh API credentials before expiry

11. Future Enhancements

11.1 Phase 2 Features

  1. Dynamic recipient selection: Override template recipients
  2. Conditional form selection: Show/hide forms based on Salesforce data
  3. Bulk sending: Send envelopes to multiple records
  4. Template caching: Cache template metadata to reduce API calls

11.2 Technical Improvements

  1. Asynchronous processing: Use @future or Queueable for large batches
  2. Retry logic: Automatic retry for transient failures
  3. Rate limit handling: Intelligent backoff and retry
  4. Analytics dashboard: Lightning component for usage reporting

12. Glossary

Term Definition
Composite Template Docusign feature to combine multiple templates into one envelope
Invocable Method Apex method callable from Screen Flows, Process Builder
Named Credential Salesforce feature for secure external credential storage
JWT JSON Web Token - authentication method for server-to-server integration
Governor Limits Salesforce platform resource limits (CPU, heap, callouts)

Document Status: Draft
Next Steps: Review with stakeholders, begin Apex development
Estimated Effort: 3-5 days development + 2 days testing


Change Log

Version Date Author Summary
1.0 2026-02-23 Paul Huliganga Initial release
1.1 2026-03-11 Paul Huliganga Added section 2.4 — multi-copy Authorization to Release Information feature; updated component diagram, Request inner class, and sequence diagram