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

40 KiB
Raw Permalink Blame History

Design — Composite Envelope Builder (Updated)

Project: Salesforce Composite Envelope Builder Version: 2.0 Date: 2026-03-25 Author: (auto-generated; update author/owner as needed)


1. Summary

This document describes the current implementation of the Composite Envelope Builder: how Screen Flows, Apex, and the dfsle toolkit collaborate to build and send a single DocuSign envelope composed from multiple DocuSign templates. It reflects the latest code and metadata in the repository (March 2026), including:

  • Flow V3 (existing, email-only path) and Flow V4 (new, SMS collection path when recipient email is blank)
  • Apex invocable: DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope and supporting classes
  • SMS delivery support for recipients without an email (via dfsle.Recipient.withSmsDelivery())
  • Multi-copy support for the "Authorization to Release Information" template (up to 5 copies)
  • Email subject & body composition rules: prefix Docusign: , truncation, greeting/signoff handling and Spanish detection
  • Key constants: SMS_PLACEHOLDER_EMAIL, MULTI_COPY_TEMPLATE_NAME

This is the canonical design doc for developers, release engineers, and reviewers.


2. Architecture Overview

High-level flow:

  • User launches Screen Flow (Flow V3 or V4)
  • User selects language (English/Spanish) and templates
  • Flow optionally collects SMS phone when recipient email is blank (V4)
  • Flow invokes Apex action Send Composite Docusign Envelope with inputs
  • Apex builds compound envelope JSON using selected templates (expands multi-copy templates as configured)
  • Apex uses dfsle toolkit to send envelope; returns envelopeId and success/error

Components:

  • Screen Flows: Docusign_Envelope_Templates_V3 (email path), Docusign_Envelope_Templates_V4 (collect SMS phone when needed)
  • Apex:
    • DocusignCompositeEnvelopeBuilder.cls (invocable entrypoint + implementation)
    • DocusignEnvelopeRequest.cls (invocable request contract)
    • DocusignEnvelopeResult.cls (invocable result contract)
    • DocusignAPIService.cls, DocusignCredentials.cls (service & credential management)
  • DocuSign: Composite Templates API (via dfsle toolkit integration)

3. Flow Behavior (V3 and V4)

3.1 Shared behavior (V3 & V4)

  • Language selection screen: user selects English or Spanish.
  • Template selection screen: multi-select list of templates filtered by selected language.
  • After selection, a loop scans selected templates to detect the multi-copy template ("Authorization to Release Information").
  • If detected, a copies screen appears (radio 15) that sets authReleaseFormCopies.
  • Flow gathers compositeTemplateIds (selected template IDs) and passes them to Apex action.

3.2 Flow V3

  • Used when standard email delivery is expected.
  • No SMS collection screen is present.
  • Behavior is unchanged from previous releases except choices include 15 copies.

3.3 Flow V4 (SMS-aware)

  • Added pre-send recipient lookup path:
    • Get_Records (Client_Case__c) → Get_Recipient_Contact (Contact lookup)
    • Is_Recipient_Email_Blank decision checks Contact.Email
    • If email is blank → SMS_Phone_Screen (field smsPhoneInput required, E.164 format recommended) → Store_SMS_Phone assignment assigns to recipientSmsPhone variable → continue
    • If email present → skip SMS_Phone_Screen and continue
  • The recipientSmsPhone variable is passed into the Apex action; when non-blank the Apex sets up SMS delivery for Docusign Recipient #1.
  • Flow element names to reference in code and docs: SMS_Phone_Screen, smsPhoneInput, Store_SMS_Phone, recipientSmsPhone.

Note: Flow metadata must keep element blocks contiguous (assignments, decisions, screens, etc.) to avoid deployment errors. The committed Docusign_Envelope_Templates_V4.flow-meta.xml already follows these rules.


4. Apex Implementation Details

4.1 Invocable contract (DocusignEnvelopeRequest)

Key input fields (invocable variables):

  • List<String> templateIds (required) — list of selected DocuSign template IDs
  • String recordId (required) — Salesforce Client Case Id
  • String language (optional) — e.g., en, es, Spanish, Español (language detection is normalized in Apex)
  • String emailSubject (optional) — optional override; Apex still prefixes with Docusign: and truncates to 100 chars
  • Integer authReleaseFormCopies (optional) — 15; counts for Authorization template
  • String recipientSmsPhone (optional) — when provided, Apex will enable SMS delivery for Docusign Recipient #1

Outputs:

  • String envelopeId
  • Boolean success
  • String errorMessage

4.2 Main behavior (DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope)

  1. Validate inputs; throw friendly errors if invalid.
  2. Expand multi-copy templates:
    • Find template IDs whose Name contains the constant MULTI_COPY_TEMPLATE_NAME.
    • If authReleaseFormCopies > 1, append (copies - 1) additional entries for each matched template ID.
    • Cap copies to 5 (Math.min(..., 5)).
  3. Build document list (documents = fromTemplate(templateId, label)) and label duplicates as (Copy N) to preserve uniqueness.
  4. Build deduplicated display names for subject/body (merge repeated templates into count markers like X (3)).
  5. Compose envelope subject & body (see section 5 for rules).
  6. Resolve recipients via resolveRecipients(recordId, recipientSmsPhone):
    • Query Client_Case__c for lookup fields
    • For Docusign Recipient #1: if linked Contact has no Email and recipientSmsPhone is provided, set recipientEmail to SMS_PLACEHOLDER_EMAIL and call recipient.withSmsDelivery(smsPhone).
  7. Use dfsle toolkit to build envelope with documents and recipients and call send.
  8. Return envelopeId and success.

4.3 Important constants

  • SMS_PLACEHOLDER_EMAIL (defined in DocusignCompositeEnvelopeBuilder.cls): placeholder email used when recipient has no email; required by DocuSign API even for SMS deliveries.
  • MULTI_COPY_TEMPLATE_NAME (defined in same class): base name used to identify the Authorization to Release Information template (covers English + Spanish variations via LIKE).

4.4 i18n detection

  • The code normalizes req.language to lowercase and treats as Spanish if any of the following are true:
    • lang.startsWith('es') (e.g. es, es-CO)
    • lang.contains('spanish')
    • lang.contains('espa') (to capture español / espanol)
  • On Spanish detection, Apex uses Spanish greeting/signoff; otherwise English is used.

5. Email Subject & Body Composition Rules

  • Subject:

    • Prefix: Docusign: is prepended to the subject to make the source explicit.
    • Truncation: After prefixing, subject is truncated to a maximum of 100 characters (Docusign requirement). The code uses left(97) + '...' if longer.
    • If user supplies emailSubject, prefix and truncation still apply.
  • Body:

    • Built as: GREETING + (template bodies joined by DIVIDER) + SIGNOFF
    • DIVIDER: "\n\n" + '─'.repeat(40) + "\n\n" (visual separator)
    • Greeting/Sign-off: English default; Spanish strings used when language detection matches Spanish variants. Examples:
      • English greeting: Hello,\n\nPlease complete the DocuSign signature request from Early Intervention Colorado.\n\n
      • Spanish greeting: Hola,\n\nPor favor, firme la solicitud de DocuSign de parte de Intervención Temprana Colorado.\n\n

6. Multi-copy Behavior

  • Single constant drives detection: MULTI_COPY_TEMPLATE_NAME = 'Authorization to Release Information'.
  • Flow shows a copies screen when the template is present; operator selects 15 copies.
  • Apex expands the templateId list before building documents to include multiple entries; duplicates are intentionally kept so each copy becomes a separate document in the envelope.
  • Document labels: duplicates are labeled with (Copy N) so they appear distinct in DocuSign Status.

7. SMS Delivery Details

  • Trigger: Flow V4 routes to SMS_Phone_Screen if the Contact linked by Docusign_Recipient_1__c has empty Email.
  • The screen collects smsPhoneInput which is assigned to recipientSmsPhone variable and passed to Apex.
  • In Apex, when recipientSmsPhone is non-blank for Docusign Recipient #1:
    • If recipient record has no email, set recipientEmail = SMS_PLACEHOLDER_EMAIL.
    • Call recipient = recipient.withSmsDelivery(smsPhone) to enable SMS delivery via dfsle toolkit.
  • Service Coordinator recipient always uses email delivery (unchanged).
  • Important setup requirement (documented): Docusign_Recipient_1__c on the Client Case must be populated with a Contact. The Contact must have Name and (for SMS flow) may have blank Email. If the Contact is null, the recipient will be omitted and envelope will be missing that signer.

8. Flows & Metadata Notes

  • Flow element naming conventions are important (screen fields bind by their name). For the SMS flow, the screen field is smsPhoneInput and the assignment must use <elementReference>smsPhoneInput</elementReference> (not ScreenName.smsPhoneInput).
  • Flow metadata must keep element blocks contiguous (screens, assignments, recordLookups, decisions, loops, choices) to avoid Salesforce Flow XML validation failures. The repo's V4 flow file was normalized accordingly.

9. Tests, Validation & CI

  • Unit tests: ensure DocusignCompositeEnvelopeBuilderTest covers:
    • English and Spanish subject/body composition
    • Multi-copy expansion with 15 copies
    • SMS flow path: when Contact.Email blank + recipientSmsPhone provided, mailbox placeholder used and withSmsDelivery applied
    • Error branches and API failures
  • CI: add a PR validation job that does:
    • sfdx auth to a validation org
    • For changed flow or Apex files, run sfdx force:source:deploy --checkonly for those components
    • Fail PR if check-only returns errors
  • Pre-deploy: run check-only deploy to target org before actual deploy. Example local command:
sfdx force:source:deploy -p force-app/main/default/flows/Docusign_Envelope_Templates_V4.flow-meta.xml,force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls -u dev-org --checkonly --testlevel NoTestRun

10. Edge Cases and Known Gotchas

  • If operator edits a flow in the org and does not retrieve+commit the changes, repo and org diverge. Use mdapi retrieve or sf project retrieve to pull the authoritative version and commit immediately.
  • Flow Builder sometimes reorders or normalizes XML: prefer re-checking contiguity of element blocks after Flow Builder saves.
  • Ensure the Flow variable recipientSmsPhone is passed to Apex action and that the assignment uses smsPhoneInput as elementReference.
  • The DocuSign API requires an email field on every recipient even when SMS is used; SMS_PLACEHOLDER_EMAIL is central and must be kept updated in Apex if changed.

11. Deployment Checklist

  1. Confirm force-app/main/default/flows/Docusign_Envelope_Templates_V4.flow-meta.xml matches the active version in org (if you intend to deploy active draft)
  2. Confirm force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls and related classes/tests are committed
  3. Run sfdx force:source:deploy --checkonly for affected components against a validation org
  4. Run Apex tests (or allow CI to run tests during deploy)
  5. Deploy to sandbox, perform a manual Flow Builder inspection of V4
  6. Activate the desired Flow version if runtime activation is needed

12. Change Log (high level)

  • 2.0 (2026-03-25): Updated design to reflect:
    • Flow V4 (SMS path) retrieval & Active flow committed
    • Spanish greeting/signoff and broadened language detection
    • Envelope subject prefix Docusign: and truncation to 100 chars
    • Increased Authorization multi-copy support to 5 copies
    • Flow element and metadata normalization

13. Contact & Ownership

  • Primary developer: Paul Huliganga (update as appropriate)
  • Repo: composite-envelope-builder
  • For deployment questions: refer to docs/DEPLOYMENT_AND_TESTING.md

This document was generated to represent the repository state as of March 25, 2026. Update the change log and ownership fields when you make future changes.

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**:

```apex
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 (15 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/4/5 Choices Radio button options with numeric values 1 / 2 / 3 / 4 / 5

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-5).'
  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, 5) : 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)" / " (Copy 4)" / " (Copy 5)" suffixes to keep document labels distinct within the envelope.


2.5 SMS Delivery Feature (v1.2)

Overview

When the primary recipient — Docusign Recipient #1 — has no email address on file, the envelope cannot be delivered via the normal email path. Rather than blocking the send, the system supports SMS delivery using the dfsle Apex Toolkit's native dfsle.Recipient.withSmsDelivery() method.

This feature is surfaced through Flow V4 (Docusign_Envelope_Templates_V4), a copy of V3 with an added pre-send phone-collection step. Flow V3 is unchanged and continues to be used for cases where the recipient has an email address.

Key Design Decisions

Decision Choice Rationale
Toolkit method dfsle.Recipient.withSmsDelivery(phone) Native dfsle support; no direct REST API needed
Email field SMS_PLACEHOLDER_EMAIL constant Docusign API requires an email on every recipient even for SMS-only delivery; placeholder satisfies this without routing any real email
Flow version New V4; V3 unchanged Preserves the working deployed flow; allows rollout at the operator's pace
Phone collection Screen in flow (not Apex) Keeps Apex free of UI logic; phone can also be supplied programmatically by bypassing the screen
Recipient scope Docusign Recipient #1 only Service Coordinator always has an email; only the primary recipient may lack one

Salesforce Data Setup Requirement

For SMS delivery to work, the Docusign Recipient #1 Contact record must exist on the Client Case — but with no email address. This is the trigger that causes the flow to route through the SMS path.

Requirement Detail
Docusign_Recipient_1__c on Client Case Must be populated with a Contact record
Contact's Email field Must be blank — the flow checks this to decide whether to collect a phone number
Contact's Name field Must be populated — Docusign uses this as the recipient's display name in the envelope

⚠️ Common mistake: Removing the Contact from Docusign_Recipient_1__c entirely (setting it to null) will cause the Apex code to skip that recipient completely — the Docusign Recipient #1 will be missing from the envelope. The Contact must be linked; only the email needs to be absent.

Configuration: Placeholder Email Constant

A single constant in DocusignCompositeEnvelopeBuilder.cls holds the placeholder email address used when the recipient has no real email. This is the only place that needs to be updated if the placeholder address ever changes.

// ============================================================
// SMS DELIVERY: Placeholder email used when the primary recipient
// (Docusign Recipient #1) has no email address and SMS delivery is
// requested via recipientSmsPhone.  Docusign requires an email on
// every recipient even when dfsle.Recipient.withSmsDelivery() is used.
// ============================================================
@TestVisible
private static final String SMS_PLACEHOLDER_EMAIL = 'placeholder_email@docusign.com';

Flow V4 Changes

The following elements were added to Docusign_Envelope_Templates_V4 (relative to V3):

Element Type Purpose
Get_Recipient_Contact Record Lookup Queries the Contact record linked to Docusign_Recipient_1__c; fetches Id, Email, Name
Is_Recipient_Email_Blank Decision Checks whether Get_Recipient_Contact.Email is null or empty
SMS_Phone_Screen Screen Collects the recipient's mobile phone number (E.164, required field); shown only when email is blank
recipientSmsPhone Variable (String) Stores the phone number entered on SMS_Phone_Screen; passed to the Apex action

Updated Flow Path (after Get_Records)

Get_Records → Get_Recipient_Contact → Is_Recipient_Email_Blank
                                          │
                                          ├─ Has Email ──────────────────────────────▶ Is_Language_Selected
                                          │
                                          └─ No Email → SMS_Phone_Screen (collect phone) ▶ Is_Language_Selected

The recipientSmsPhone variable is passed to Send_Composite_Envelope as a new input parameter. When the recipient has an email the variable is blank and the Apex action behaves identically to V3.

Apex Changes

DocusignEnvelopeRequest.cls — new @InvocableVariable:

@InvocableVariable(
    label='Recipient SMS Phone'
    description='Mobile phone number for SMS delivery when Docusign Recipient #1 has no email. 
                 A placeholder email is substituted automatically. Format: +15551234567 (E.164 preferred).'
    required=false
)
global String recipientSmsPhone;

DocusignCompositeEnvelopeBuilder.clsresolveRecipients now accepts smsPhone and passes it only to the Docusign Recipient #1 builder:

private static List<dfsle.Recipient> resolveRecipients(String recordId, String smsPhone) {
    // ... query Client_Case__c ...
    // Service Coordinator — always email delivery (null smsPhone)
    recipients.add(buildRecipient(serviceCoordinatorId, ROLE_SERVICE_COORDINATOR, routingOrder++, recordId, null));
    // Docusign Recipient #1 — SMS delivery when smsPhone is provided
    recipients.add(buildRecipient(docusignRecipientId, ROLE_DOCUSIGN_RECIPIENT, routingOrder++, recordId, smsPhone));
}

buildRecipient applies the SMS toolkit method and placeholder email when a phone is supplied:

private static dfsle.Recipient buildRecipient(
    Id recipientId, String roleName, Integer routingOrder,
    String sourceRecordId, String smsPhone) {

    // ... resolve name/email from Contact or User ...

    if (String.isBlank(recipientEmail)) {
        if (String.isNotBlank(smsPhone)) {
            recipientEmail = SMS_PLACEHOLDER_EMAIL; // satisfy API requirement
        } else {
            throw new IllegalArgumentException('No email found for ' + roleName + ' ...');
        }
    }

    dfsle.Recipient recipient = dfsle.Recipient.fromSource(
        recipientName, recipientEmail, null, roleName, new dfsle.Entity(sourceRecordId)
    );

    if (String.isNotBlank(smsPhone)) {
        recipient = recipient.withSmsDelivery(smsPhone); // dfsle native SMS delivery
    }

    return recipient;
}

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);

4.3 Email subject and body composition

The Apex layer composes the envelope's email subject and body before sending. Key rules:

  • Subject: Prefixed with Docusign: to make the source explicit for recipients. The final subject is truncated to 100 characters (Docusign requirement).
  • Body: Built as Greeting → template bodies (joined with a visual divider) → Sign-off. The divider is a short visual separator (\n\n + 40 characters + \n\n).
  • Greeting/Sign-off: Support English (default) and Spanish. The Flow's language input may be a locale code (es, es-CO) or a user-friendly string (Spanish, Español). The code normalizes and accepts common Spanish forms and uses Spanish greeting/signoff when detected.

Example (English):

Greeting: Hello,\n\nPlease complete the DocuSign signature request from Early Intervention Colorado.\n\n

Sign-off: \n\nThank you,\nEarly Intervention Colorado

Example (Spanish):

Greeting: Hola,\n\nPor favor, firme la solicitud de DocuSign de parte de Intervención Temprana Colorado.\n\n

Sign-off: \n\nGracias,\nIntervención Temprana Colorado

Note: If the Flow's language value is blank or unrecognized, English is used as the default.


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
1.2 2026-03-13 Paul Huliganga Added section 2.5 — SMS delivery via dfsle.Recipient.withSmsDelivery() for recipients without email; added Flow V4 description, SMS_PLACEHOLDER_EMAIL constant, recipientSmsPhone parameter; increased multi-copy support to 5 copies; prefixed envelope subject with Docusign: and truncated to 100 chars; added Spanish greeting/signoff and expanded language detection (e.g., 'es', 'es-CO', 'Spanish', 'Español').