Open 5. Workflow → Documents → Certificate Templates and click Design New (or upload an HTML template). Each template has a code and a language; the GENERATE_CERTIFICATE action references it by code.

Workflow → Documents → Certificate Templates → Design New

The demo's template TE4I3DV in Documents

Elements: Text, Image, Table, Rectangle, QR Code, Line
The designer's elements
| Element | Use it for |
|---|---|
| Text | Titles, body, legal wording |
| Image | Coat of arms, logos, seals, signatures (JPG, PNG, GIF, SVG) |
| Table | Lists and structured data |
| Rectangle | Borders and backgrounds |
| QR Code | Verification link |
| Line | Separators and signature lines |
The Fields panel lists your form fields. Dropped on the canvas they become placeholders such as ${industrialPark}, replaced by the applicant's answers when the certificate is generated.

Fields: every form field can be placed

Placeholders in the text

Text properties: font, size, weight, alignment, colour

Image properties: source, alt text, fit, border

The final certificate
▶ Video walkthrough in the Rise course: Watch how to design a certificate: add elements like text, images, tables, QR codes and lines, insert dynamic fields that pull data from your form, and style everything for a professional finish.

Preview of TE4I3DV

GENERATE_CERTIFICATE on APPROVE references the template code
Placeholders (FreeMarker). The !"" prints nothing instead of failing when a value is missing
| Placeholder | Value |
|---|---|
${applicationNumber!""} |
The application number |
${FullnamesCD10!""} |
A text field: ${<field key>} |
${.vars["DISTRICTCD12.label"]!""} |
The label of a dropdown or location field |
${certificate_expiration!""} |
Expiry date (from certificateExpirationDays) |
${certificate_validation_link!""} |
Verification link, for the QR code |
| Also available | approve_date, officeName, price, currency, billReferenceNumber, paymentMethod, applicantName |

First demo certificate: district and sector are empty
Gotcha · dotted keys need .vars
Dropdown and location answers are stored under dotted keys (
DISTRICTCD12.label). FreeMarker reads${(DISTRICTCD12.label)!""}as "the label property of DISTRICTCD12", finds nothing, and the!""silently prints an empty string: exactly the bug above. Write${.vars["DISTRICTCD12.label"]!""}. Preview with a real test application, not only the designer's preview.
Gotcha · always set certificateExpirationDays
The engine adds certificateExpirationDays to today's date. Leave it out and GENERATE_CERTIFICATE fails (
CERTIFICATE_GENERATION_ACTION_ERROR); since it is breaking, APPROVE fails. Use 0 for a certificate that never expires. The demo uses 7 days so testers can see the expiry date.
The certificate shows an empty sector although the applicant chose one. The template has ${(SECTORCD12.label)!""}. What do you change?
D. The dotted key must be read through .vars. Removing !"" would turn the empty value into a generation error.
Tip · reference
← B8c · Branching, custom states and chained events · B10 · Assignment 2: build a medium service →