# API Reference **Project**: Salesforce Composite Envelope Builder **Version**: 1.2 **Date**: February 23, 2026 (updated March 13, 2026) --- ## 1. Docusign REST API Integration ### 1.1 Base Endpoint **Production**: ``` https://na3.docusign.net/restapi/v2.1 ``` **Sandbox**: ``` https://demo.docusign.net/restapi/v2.1 ``` **Note**: Replace `na3` with your account's data center (na2, na3, eu1, etc.) --- ## 2. Create Composite Envelope ### 2.1 HTTP Request **Method**: POST **Endpoint**: `/accounts/{accountId}/envelopes` **Content-Type**: `application/json` **Authorization**: `Bearer {access_token}` ### 2.2 Request Headers ```http POST /restapi/v2.1/accounts/12345678-abcd-1234-abcd-1234567890ab/envelopes HTTP/1.1 Host: na3.docusign.net Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY4MTg... Content-Type: application/json Accept: application/json ``` ### 2.3 Request Body (Composite Templates) #### Minimum Example (2 templates) ```json { "status": "sent", "emailSubject": "Please review and sign these forms", "compositeTemplates": [ { "compositeTemplateId": "1", "serverTemplates": [ { "sequence": "1", "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ] }, { "compositeTemplateId": "2", "serverTemplates": [ { "sequence": "2", "templateId": "b2c3d4e5-f6g7-8901-bcde-fg2345678901" } ] } ] } ``` #### Advanced Example (with merge fields) ```json { "status": "sent", "emailSubject": "Please review and sign these forms", "compositeTemplates": [ { "compositeTemplateId": "1", "serverTemplates": [ { "sequence": "1", "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ], "inlineTemplates": [ { "sequence": "1", "customFields": { "textCustomFields": [ { "name": "SalesforceRecordId", "value": "0018V00000ABC123" }, { "name": "CaseNumber", "value": "00001234" } ] } } ] } ] } ``` ### 2.4 Response #### Success (201 Created) ```json { "envelopeId": "f9876543-21ab-cdef-0123-456789abcdef", "uri": "/envelopes/f9876543-21ab-cdef-0123-456789abcdef", "statusDateTime": "2026-02-23T12:34:56.789Z", "status": "sent" } ``` #### Error (400 Bad Request) ```json { "errorCode": "INVALID_REQUEST_PARAMETER", "message": "The request contained at least one invalid parameter. The template with ID 'invalid-id' does not exist." } ``` #### Error (401 Unauthorized) ```json { "errorCode": "USER_AUTHENTICATION_FAILED", "message": "One or both of Username and Password are invalid." } ``` --- ## 3. Composite Template Structure ### 3.1 Key Concepts **compositeTemplates** (array): - Each element represents a server template to include in the envelope - Order in array determines document order in envelope **compositeTemplateId** (string): - Unique identifier for this composite template within the request - Can be any string (recommend using sequence numbers: "1", "2", "3") **serverTemplates** (array): - References pre-existing templates in your Docusign account - Must include `templateId` (GUID from Docusign) **sequence** (string): - Determines document order within the envelope - "1" = first document, "2" = second, etc. - **Important**: Use strings, not integers **inlineTemplates** (array, optional): - Allows runtime override of template values - Used for custom fields, recipient data, tabs ### 3.2 Recipient Merging **Automatic Merge**: When multiple templates have recipients with the same `roleName`, Docusign automatically merges them into a single recipient. **Example**: - Template A has recipient role: "Signer" - Template B has recipient role: "Signer" - Result: One recipient signs all documents **Requirements for merge**: - Exact match on `roleName` - Same recipient `routingOrder` (signing order) - Merge happens automatically, no additional configuration needed --- ## 4. Authentication ### 4.1 JWT (JSON Web Token) **Preferred method for server-to-server integration** #### Step 1: Generate JWT ```apex // Apex pseudocode String jwt = generateJWT( integrationKey, // From Docusign Apps & Keys userId, // Docusign user GUID rsaPrivateKey, // RSA private key (PEM format) 'https://account-d.docusign.com/oauth/auth', // Sandbox 3600 // Token expiry (1 hour) ); ``` #### Step 2: Exchange JWT for Access Token **Request**: ```http POST /oauth/token HTTP/1.1 Host: account-d.docusign.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={jwt} ``` **Response**: ```json { "access_token": "eyJ0eXAiOiJNVCIsImFsZyI...", "token_type": "Bearer", "expires_in": 3600 } ``` #### Step 3: Use Access Token ```http Authorization: Bearer eyJ0eXAiOiJNVCIsImFsZyI... ``` ### 4.2 OAuth2 Authorization Code **User-based authentication** (not recommended for this use case) See [Docusign OAuth2 documentation](https://developers.docusign.com/platform/auth/authcode/) for details. --- ## 5. Error Codes ### 5.1 Common Error Codes | Error Code | HTTP | Meaning | Resolution | |------------|------|---------|------------| | `INVALID_REQUEST_PARAMETER` | 400 | Invalid parameter in request | Check JSON structure, template IDs | | `USER_AUTHENTICATION_FAILED` | 401 | Invalid or expired access token | Refresh access token | | `USER_LACKS_PERMISSIONS` | 403 | User doesn't have permission | Check Docusign user permissions | | `RESOURCE_NOT_FOUND` | 404 | Template ID not found | Verify template exists in account | | `DUPLICATE_RESOURCE` | 409 | Duplicate request | Check for duplicate envelope | | `ONESIGNAL_GENERIC_ERROR` | 500 | Docusign server error | Retry after delay | ### 5.2 Rate Limits **Hourly limit**: Varies by plan (1,000 - 10,000+ API calls/hour) **Response header when nearing limit**: ```http X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 50 X-RateLimit-Reset: 1614358800 ``` **Error when limit exceeded**: ```json { "errorCode": "HOURLY_APIINVOCATION_LIMIT_EXCEEDED", "message": "The maximum number of hourly API invocations has been exceeded." } ``` **Mitigation**: - Implement exponential backoff - Cache access tokens (don't generate new token for each request) - Batch operations when possible --- ## 6. Custom Fields ### 6.1 Text Custom Fields **Use case**: Store Salesforce record ID in envelope ```json { "compositeTemplates": [ { "inlineTemplates": [ { "customFields": { "textCustomFields": [ { "name": "SalesforceRecordId", "value": "0018V00000ABC123", "show": "false", "required": "false" } ] } } ] } ] } ``` ### 6.2 List Custom Fields ```json { "customFields": { "listCustomFields": [ { "name": "FormLanguage", "value": "English", "listItems": ["English", "Spanish"] } ] } } ``` --- ## 7. Webhook Configuration ### 7.1 Connect Webhook (for document retrieval) **Not required for Phase 1**, but useful for future automation. **Endpoint**: Configure in Docusign Admin → Connect → Add Configuration **Webhook payload** (when envelope completes): ```json { "event": "envelope-completed", "apiVersion": "v2.1", "uri": "/restapi/v2.1/accounts/123/envelopes/abc", "envelopeId": "f9876543-21ab-cdef-0123-456789abcdef", "envelopeSummary": { "status": "completed", "emailSubject": "Please review and sign", "completedDateTime": "2026-02-23T14:30:00Z" } } ``` --- ## 8. Template Management APIs ### 8.1 List Templates **Use case**: Retrieve template list for Flow dropdown **Request**: ```http GET /restapi/v2.1/accounts/{accountId}/templates?count=100&order=name&order_by=asc ``` **Response**: ```json { "resultSetSize": "14", "totalSetSize": "14", "envelopeTemplates": [ { "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Form A - English", "shared": "true", "description": "English version of Form A" }, { "templateId": "b2c3d4e5-f6g7-8901-bcde-fg2345678901", "name": "Form B - English", "shared": "true", "description": "English version of Form B" } ] } ``` ### 8.2 Get Template Details **Use case**: Retrieve template metadata (recipients, tabs) **Request**: ```http GET /restapi/v2.1/accounts/{accountId}/templates/{templateId} ``` **Response**: ```json { "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Form A - English", "recipients": { "signers": [ { "roleName": "Signer 1", "recipientId": "1", "routingOrder": "1" }, { "roleName": "Signer 2", "recipientId": "2", "routingOrder": "2" } ] }, "documents": [ { "documentId": "1", "name": "Form_A.pdf", "pages": "2" } ] } ``` --- ## 9. Apex HTTP Callout Example ### 9.1 Complete Callout ```apex public static String createCompositeEnvelope(String envelopeJSON, String accessToken, String accountId) { HttpRequest req = new HttpRequest(); req.setEndpoint('callout:DocusignAPI/accounts/' + accountId + '/envelopes'); req.setMethod('POST'); req.setHeader('Authorization', 'Bearer ' + accessToken); req.setHeader('Content-Type', 'application/json'); req.setHeader('Accept', 'application/json'); req.setBody(envelopeJSON); req.setTimeout(120000); // 120 seconds Http http = new Http(); HttpResponse res = http.send(req); if (res.getStatusCode() == 201) { Map responseMap = (Map) JSON.deserializeUntyped(res.getBody()); return (String) responseMap.get('envelopeId'); } else { throw new CalloutException('Docusign API error [' + res.getStatusCode() + ']: ' + res.getBody()); } } ``` ### 9.2 Named Credential Configuration **Setup → Named Credentials → New Named Credential** **Settings**: - **Label**: Docusign API - **Name**: DocusignAPI - **URL**: `https://na3.docusign.net/restapi/v2.1` - **Identity Type**: Named Principal - **Authentication Protocol**: OAuth 2.0 - **Scope**: `signature impersonation` - **Token Endpoint**: `https://account-d.docusign.com/oauth/token` (sandbox) - **JWT Token Exchange**: Enabled **Usage in Apex**: ```apex req.setEndpoint('callout:DocusignAPI/accounts/' + accountId + '/envelopes'); ``` --- ## 10. Testing with Postman ### 10.1 Import Docusign Collection Docusign provides an official Postman collection: https://github.com/docusign/postman-collections ### 10.2 Create Composite Envelope Test **Request**: ``` POST {{baseUrl}}/accounts/{{accountId}}/envelopes Headers: Authorization: Bearer {{accessToken}} Content-Type: application/json Body: { "status": "sent", "emailSubject": "Test Composite Envelope", "compositeTemplates": [ { "compositeTemplateId": "1", "serverTemplates": [ { "sequence": "1", "templateId": "{{templateId1}}" } ] }, { "compositeTemplateId": "2", "serverTemplates": [ { "sequence": "2", "templateId": "{{templateId2}}" } ] } ] } ``` --- ## 11. SMS Delivery via dfsle Apex Toolkit (v1.2) ### 11.1 Overview Rather than calling the Docusign REST API directly for SMS delivery, this project uses the native **dfsle Apex Toolkit** method `dfsle.Recipient.withSmsDelivery()`, which is part of the Docusign for Salesforce managed package already installed in the org. This approach requires no additional REST endpoints, no extra authentication, and no HTTP callouts beyond what the toolkit already handles. ### 11.2 `dfsle.Recipient.withSmsDelivery(phone)` **Method signature**: ```apex dfsle.Recipient withSmsDelivery(String phone) ``` **Parameters**: | Parameter | Type | Description | |-----------|------|-------------| | `phone` | `String` | Mobile phone number in E.164 format (e.g. `+15551234567`). International numbers must include the country code. | **Returns**: A new `dfsle.Recipient` instance with SMS delivery configured (`deliverBySms = true`). **Important notes**: - This method sets the delivery method for the signing invitation; it is **not** a 2FA/authentication method. - The Docusign API still requires an `email` field on every recipient even when SMS delivery is configured. When the recipient contact has no email address, a placeholder (`placeholder_email@docusign.com`) is substituted automatically by the Apex layer. No actual email is sent to this address. - Only applied to **Docusign Recipient #1**. The Service Coordinator always uses email delivery. ### 11.3 Invocable Action Parameter The `recipientSmsPhone` input parameter on the `Send_Composite_Envelope` Apex action activates SMS delivery: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `recipientSmsPhone` | `String` | No | Mobile phone number for SMS delivery. When blank (or not provided), the envelope is sent normally by email. E.164 format preferred: `+15551234567`. | ### 11.4 Apex Usage Pattern ```apex // Resolve email from Contact record String recipientEmail = contact.Email; // Substitute placeholder when email is blank and SMS phone is provided if (String.isBlank(recipientEmail) && String.isNotBlank(smsPhone)) { recipientEmail = 'placeholder_email@docusign.com'; } // Build recipient using dfsle toolkit dfsle.Recipient recipient = dfsle.Recipient.fromSource( contact.Name, recipientEmail, null, // phone param (not used here) 'Docusign Recipient #1', // role name — must match template exactly new dfsle.Entity(caseRecordId) // source record for merge fields ); // Enable SMS delivery when phone is provided if (String.isNotBlank(smsPhone)) { recipient = recipient.withSmsDelivery(smsPhone); } ``` ### 11.5 Flow V4 Integration In `Docusign_Envelope_Templates_V4`, the flow checks the recipient contact's email before presenting the send screen: 1. **`Get_Recipient_Contact`** — queries the Contact linked to `Docusign_Recipient_1__c` 2. **`Is_Recipient_Email_Blank`** — if email is null or empty, routes to the phone collection screen; otherwise proceeds normally 3. **`SMS_Phone_Screen`** — collects the mobile number (required text field); stores result in the `recipientSmsPhone` flow variable 4. **`Send_Composite_Envelope`** action — receives `recipientSmsPhone` as an input; when non-blank the Apex layer applies `withSmsDelivery()` --- ## 12. Reference Links - [Docusign REST API Reference](https://developers.docusign.com/docs/esign-rest-api/reference/) - [Composite Templates Guide](https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-composite-template/) - [JWT Authentication](https://developers.docusign.com/platform/auth/jwt/) - [Salesforce Named Credentials](https://help.salesforce.com/s/articleView?id=sf.named_credentials_about.htm) - [Salesforce HTTP Callouts](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_httprequest.htm) --- **Document Version**: 1.2 **Last Updated**: March 13, 2026 **Change Log**: | Version | Date | Summary | |---------|------|---------| | 1.0 | 2026-02-23 | Initial release | | 1.1 | 2026-03-11 | No changes (version aligned with design/requirements) | | 1.2 | 2026-03-13 | Added section 11 — SMS delivery via `dfsle.Recipient.withSmsDelivery()`; documented `recipientSmsPhone` parameter; renumbered Reference Links to section 12 |