Generate documents from Apex

Last updated 25 October 2026.

At the end your Apex code makes PDF and editable Word documents for one record or many, and follows, cancels or retries each run. For Salesforce developers.

Before you start

  • The code runs as someone with Docable Generate and Docable Runtime Generator Storage. The caller is always the running user.
  • The template is active. In an installed package, prefix the class with the package namespace, for example ns.DcsGenerateV1.

Steps

  1. Find the template’s Id and the record’s Id.
  2. Build a DcsGenerateV1.Request: set templateId, recordIds and outputs, and a new requestKey. Optional: set brandKit to one of the template’s Brand Kits, by name or Id, to print with it instead of the template’s own choice.
  3. Call DcsGenerateV1.generate(request).
  4. Read result.success. If it is true and result.completed is true, the Files are in result.documents. Otherwise follow the run with DcsGenerateV1.status(result.runId).

Every field is in Generation request and result fields.

Example

One work order, a PDF and a Word file:

Id templateId = [SELECT Id FROM DcsTemplate__c WHERE Name = 'Branded service report' WITH USER_MODE LIMIT 1].Id;
Id workOrderId = [SELECT Id FROM WorkOrder WITH USER_MODE ORDER BY WorkOrderNumber LIMIT 1].Id;

DcsGenerateV1.Request request = new DcsGenerateV1.Request();
request.templateId = templateId;
request.recordIds = new List<Id>{workOrderId};
request.outputs = new List<String>{'PDF', 'DOCX'};
request.requestKey = UUID.randomUUID().toString(); // send the same key again to get this run back, not a second one
// Optional: request.brandKit = 'HelioGrid'; prints with that one of the template's Brand Kits instead of its own choice.

DcsGenerateV1.Result result = DcsGenerateV1.generate(request);
if (result.success != true) {
    System.debug('Refused: ' + result.reasonCode + ' ' + result.message + ' Next: ' + result.nextAction);
} else if (result.completed == true) {
    for (DcsGenerateV1.GeneratedFile file : result.documents[0].files) {
        System.debug(file.format + ' ' + file.fileName + ' ContentDocument ' + file.contentDocumentId);
    }
} else {
    System.debug('Queued run ' + result.runId + ' (' + result.state + ')');
}

Three work orders, one background run, one PDF each and a combined PDF printed two-sided:

List<Id> workOrderIds = new List<Id>(new Map<Id, WorkOrder>([SELECT Id FROM WorkOrder WITH USER_MODE
    ORDER BY WorkOrderNumber LIMIT 3]).keySet());

DcsGenerateV1.Request request = new DcsGenerateV1.Request();
request.templateId = templateId;
request.recordIds = workOrderIds;
request.outputs = new List<String>{'PDF'};
request.combine = true;   // one extra PDF with every document in record order
request.duplex = true;    // a blank page after any document with an odd number of pages
request.requestKey = UUID.randomUUID().toString();

DcsGenerateV1.Result result = DcsGenerateV1.generate(request);
System.debug(result.success == true ? 'Run ' + result.runId + ': ' + result.state + ', ' + result.itemCount + ' records'
    : 'Refused: ' + result.reasonCode + ' ' + result.message);

More than one record is always made in the background, so the call returns a queued run. A request can carry any number of record Ids of one object; about 40,000 fit in one call made while someone waits and about 80,000 in a background one. For more, split the Ids into several requests. Follow, cancel or retry a run:

DcsGenerateV1.Result run = DcsGenerateV1.status(runId);
System.debug(run.state + ': ' + run.succeededCount + ' of ' + run.itemCount + ' done, ' + run.failedCount + ' failed');
for (DcsGenerateV1.GeneratedDocument document : run.documents) {
    for (DcsGenerateV1.GeneratedFile file : document.files) System.debug(document.recordId + ' ' + file.format + ' ' + file.state);
}
if (run.combined != null) System.debug('Combined PDF ' + run.combined.contentDocumentId);
// DcsGenerateV1.cancel(runId) stops documents that have not started; DcsGenerateV1.retry(runId) makes failed ones again.

Check it worked

The first example prints a PDF and a Word file name with their ContentDocument Ids, and both are Files on the work order. The second prints a run Id and a state. status shows Succeeded, 3 of 3 done, and the combined PDF.

If it doesn’t work

What you see What to do
result.success is false. Nothing throws. Read reasonCode, message and nextAction, and look the code up in Error and reason codes.
result.completed is not true. The run is queued. Follow it with status(runId). A queued or running run is never success.
The same request runs twice. Send a new lowercase UUID as requestKey for each request. The same key returns the same run with replayed true.

Next

Something unclear or wrong? Tell support. Product page: Automation and API.