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
- Find the template’s Id and the record’s Id.
- Build a
DcsGenerateV1.Request: settemplateId,recordIdsandoutputs, and a newrequestKey. Optional: setbrandKitto one of the template’s Brand Kits, by name or Id, to print with it instead of the template’s own choice. - Call
DcsGenerateV1.generate(request). - Read
result.success. If it is true andresult.completedis true, the Files are inresult.documents. Otherwise follow the run withDcsGenerateV1.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.