Generate documents using compose API

Documill Dynamo offers an HTTP compose API that allows document generation to be automated. The API can also be used from links and buttons so that a document is composed and returned directly without any additional user interface or user intervention like the custom button in Salesforce.


  • This API is not included in the default product. API usage is available as an add on feature and is priced per API call. Contact us if you would like to use API for your solution.
  • If the API limit is exceeded by a large margin, the API access may be suspended.
  • API rate limit is 1 call in 3 seconds per user

How does Compose API work?

The Compose API expects a HTTP request that contains all the information necessary to generate the document. The template and other data is loaded from Salesforce with the credentials provided by one of the two options: sessionID and serverURL parameters or OAuth authentication process.

The response from the API depends on the provided parameters and template logic. By default, the API returns nothing and the response is HTTP 200 OK. The template can be set to return files with the returnDocument command, or text with the returnString command.

When composing a template with the returnDocument command, the API can be configured to return a link to the result file instead of the file data being included in the response. If the getLink parameter is "true", the response is a JSON object with the key "url", whose value is a URL that can be used to download the result document. The file is available via the URL for 5 minutes after calling the API.

During testing it can be useful to set the getLogs parameter to value "true". This makes the API return a JSON array of errors instead of the command-defined result. An empty array is returned if there were no errors. The log output can be further configured with the logLevel parameter to return also warnings and informational messages.


Name Value Required Example Notes
action compose Yes action=compose
This identifies which API is used and has to be written exactly as in the Value-column.
contentDisposition inline | attachment
Can be used to set the "Content-Disposition" header of the response if the result was defined with the returnDocument command.
contentType A MIME type.
Can be used to set the "Content-Type" header of the response if the result was defined with the returnString command. By default, this header's value is "text/plain". 
getLink Values trueor false. Is false by default if not included in the request.
Can be used to return a link to the result document instead of the document itself if the result was defined with the returnDocument command.
getLogs Values true or false. Is false by default if not included in the request.
Can be used to return compose logs instead of the result document. This is useful for debugging documents used with the API. Returned logs are in JSON format. This parameter takes precedence over getLink.
Can be used to provide more logging information. Default is SEVERE where only serious errors are reported.
serverURL Salesforce SOAP API endpoint URL. The URL should be percent encoded.
Can be included in the request to specify the Salesforce instance endpoint.
sessionID Salesforce session ID.

Can be included in the request to use the sessionID directly instead of OAuth.
sfEnvironment sandbox
Has to be included in the request when using OAuth and using a sandbox instance.
Salesforce record ID of the template. (ContentDocument, Attachment, Document)
The user has to have read access to this file. Supported types are ContentDocument, Attachment and Document.

You can add additional parameters to pass information to your template. These parameters can be accessed in the template with the ${param.param_name} notation.

Remote site settings

Before outbound API calls can be invoked from Salesforce, a remote site must be added.

Go to Setup -> Security -> Remote Site Settings -> New Remote Site

Add new site with URL: 

Integration with Salesforce Process Builder

Dynamo compose API can be integrated with Process Builder by using special Apex action class. Source code of the class is included below. This code can be copied to Salesforce organization by adding new Apex class.

* Helper class to invoke Dynamo API from Process Builder.

global class DynamoAPIHelper {
  @InvocableMethod(label='Dynamo API' description='Dynamo API for Process Builder')
  public static List<DynamoAPIResult> callDynamoInvocable(List<DynamoAPIRequest> requestList) 
     DynamoAPIRequest request = requestList[0];
     List<DynamoAPIResult> results = new List<DynamoAPIResult>();   
     DynamoAPIResult result = new DynamoAPIResult();
     result.status = 'Asynchronous Dynamo API call. Template ID=' + request.templateID;
     return results;
  public static void sendRequestAsync(String templateID,String recordID,String paramName,String paramValue,String eventID,boolean getLogs,String endPoint,String sessionID,String partnerServerURL) 
    HttpRequest req = new HttpRequest();
    if (String.isBlank(endPoint))
      endPoint = '';
    String body = buildBody(templateID,recordID,paramName,paramValue,eventID,getLogs,endPoint,sessionID,partnerServerURL);
    Http http = new Http();
    HTTPResponse res = http.send(req);
  private static String buildBody(String templateID,String recordID,String paramName,String paramValue,String eventID,boolean getLogs,String endPoint,String sessionID,String partnerServerURL) 
    if (String.isBlank(eventID)) {
      eventID = 'new';
    if (String.isBlank(partnerServerURL)) {
      partnerServerURL = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/u/43.0/' + UserInfo.getOrganizationId();
    if (String.isBlank(sessionID)) {
      sessionID = UserInfo.getSessionId();
    List<String> l = new List<String>();
    l.add('&sessionID=' + sessionID);
    l.add('&serverURL=' + partnerServerURL);
    l.add('&templateID=' + templateID);
    l.add('&eventID=' + eventID);
    l.add('&id=' + recordID);
    if (!String.isBlank(paramName) && !String.isBlank(paramValue)) {
        l.add('&' + paramName + '=' + paramValue);
    l.add('&getLogs=' + getLogs);
    String body = String.join(l,'');
    return body;
  global class DynamoAPIRequest {
    global String templateID;

    global String recordID;

    global String paramName;    
    global String paramValue;    
    global String eventID;
    global boolean getLogs;
    global String endPoint;  
    global String sessionID;  
    global String partnerServerURL;  
  global class DynamoAPIResult {
    global String status;


When Apex class has been added it will be visible in Process Builder.

When editing Immediate Action step, choose Apex Action and Apex class Dynamo API.

Define Dynamo API Apex variables recordID and templateID (both are required).

When ready your process should look like example above. When process is activated it's ready to call Dynamo API when defined conditions are met.

Process builder initiated API calls will return immediately (asynchronous call) and can't return any values, however initiated template logic itself can update fields and create records.

Direct call

This example URL generates a document from template 069D0000003e56o with record ID 006D000000LSeWD. The generated document is downloaded directly from the link.

Direct call from Apex

This example shows how to send POST request to Dynamo API using APEX code.
Request includes one custom paramater json. It's used to pass JSON coded value to Dynamo template.

If executed template should return document then the template logic should have returnDocument command.

  public void sendRequest() {
    HttpRequest req = new HttpRequest();
    String json = '{"field1" : "field1 Value", "field2" : "field2 Value"}';
    String templateID = 'xxxxxxxxxxxx';
    String serverURL = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/u/43.0/' + UserInfo.getOrganizationId();

    req.setBody('action=compose' +
                '&json=' + json +
                '&templateID=' + templateID +
                '&serverURL=' + serverURL + 
                '&sessionID=' + UserInfo.getSessionId());     
    Http http = new Http();
    HTTPResponse res = http.send(req);
    // Handle response here