27.1 Overview
Business processes produce documents. Award notices, inspection certificates, denial letters, compliance attestations, invoices, shipping manifests. ADL templates declare these documents in YAML — the trigger that generates them, the data they contain, the template that formats them, and the storage that archives them.
Templates connect the existing ADL infrastructure: resources provide the data, history events provide the trigger, AEL provides the computation, notifications provide the delivery, and schedules provide the automation. The template adds the formatting and the file output.
27.2 Declaration
Templates are declared in the domain-level templates: block:
domain:
name: awards
version: "2.0.0"
templates:
awardNotice:
description: "Official notice of award generated upon activation"
trigger:
resource: Award
event: transition
transition: activate
format: pdf
filename: "Award-Notice-{awardNumber}-{$date}.pdf"
permissions:
generate: [spoOfficer, admin]
view: [authenticated]
data:
award: $record
pi:
source: Researcher
filter: "id == $record.piId"
single: true
budgetCategories:
source: BudgetCategory
filter: "awardId == $record.id"
sort: category asc
institution:
static:
name: "${INSTITUTION_NAME}"
address: "${INSTITUTION_ADDRESS}"
layout:
pageSize: letter
margins: { top: 1in, right: 1in, bottom: 1in, left: 1in }
header:
logo: "${INSTITUTION_LOGO_PATH}"
text: "Office of Sponsored Programs"
footer:
left: "Generated {$datetime}"
center: "Page {$page} of {$pages}"
right: "CONFIDENTIAL"
body: |
# Notice of Award
**Award Number:** {award.awardNumber}
**Date Issued:** {$date}
## Award Information
| Field | Value |
|-------|-------|
| Title | {award.title} |
| Sponsor | {award.sponsor} |
## Principal Investigator
**Name:** {pi.firstName} {pi.lastName}
**Department:** {pi.department}
## Budget Summary
| Category | Budgeted |
|----------|----------|
{#each budgetCategories}
| {category} | {budgetedAmount | money} |
{/each}
**Total Award:** {award.totalAmount | money}
attachTo:
resource: Award
recordId: $record.id
field: awardNoticeDocId
notification:
channels: [email]
recipients:
field: piId
template:
subject: "Award Notice — {award.awardNumber}"
body: "Please find the attached notice of award."
attachDocument: true
Template Properties
| Property | Type | Required | Description |
|---|---|---|---|
description | string | no | Human-readable description |
trigger | object or "manual" | yes | Event trigger or manual generation |
condition | string | no | AEL expression — only generate when true |
format | string | yes | Output format: pdf, docx, html, markdown |
filename | string | yes | Output filename with {field} interpolation |
permissions.generate | string[] | yes | Roles that can trigger generation |
permissions.view | string[] | yes | Roles that can view generated documents |
parameters | array | if manual | Input parameters for on-demand generation |
data | object | yes | Data context assembly |
layout | object | no | Page layout configuration (PDF/DOCX) |
body | string | yes | Template body with interpolation |
attachTo | object or "none" | no | Auto-attach to a resource record |
notification | object | no | Send notification with document attached |
27.3 Trigger Types
Event-Driven
Generates automatically when a history event fires:
trigger:
resource: Award
event: transition
transition: activate
The template subscribes to history events on the bus. When the matching event fires, the runtime assembles the data context, interpolates the template, renders the output, stores the document, and optionally sends a notification.
Manual
Generated on demand via API call:
trigger: manual
parameters:
- name: assetId
type: uuid
required: true
label: "Asset"
- name: includeHistory
type: boolean
default: true
label: "Include event history"
Manual templates are invoked through the API with parameter values:
POST /api/v1/{domain}/templates/assetPassport/generate
{ "assetId": "asset-001", "includeHistory": true }
Scheduled
Generated by a schedule definition (§24) using action: template:
schedules:
monthlyComplianceReport:
recurrence: monthly on 1st at 06:00
action: template
template: complianceMonthlyReport
Conditional
Any trigger can include a condition that gates generation:
trigger:
resource: LoanApplication
event: transition
transition: deny
condition: "$record.denialReasonCode != 'withdrawn'"
The condition is evaluated after the event fires. If false, no document is generated.
27.4 Data Context Assembly
The data block defines what data is available to the template body. Each key becomes a variable accessible via {keyName.field} interpolation.
Data Source Types
$record — the triggering record:
data:
award: $record
Resource query — fetch related records:
data:
lineItems:
source: OrderItem
filter: "orderId == $record.id"
sort: createdAt asc
customer:
source: Customer
filter: "id == $record.customerId"
single: true
When single: true, the query returns one record. Otherwise it returns an array for {#each}.
Static values — environment variables and constants:
data:
institution:
static:
name: "${INSTITUTION_NAME}"
address: "${INSTITUTION_ADDRESS}"
Computed values — AEL expressions:
data:
totalExpended:
computed: "sum(Expenditure, 'amount', awardId == $record.id && status == 'processed')"
History events — recent event timeline:
data:
recentHistory:
history: true
resource: $record
limit: 100
27.5 Template Body Syntax
The template body uses Markdown with interpolation expressions.
Field Interpolation
{award.awardNumber}
{pi.firstName} {pi.lastName}
{$date}
Format Pipes
{award.totalAmount | money} → $500,000.00
{utilizationRate | percentage} → 62.5%
{award.startDate | date} → Mar 1, 2026
Format pipes use the same format system as reports (§26.5).
Iteration
{#each budgetCategories}
| {category} | {budgetedAmount | money} |
{/each}
Special variables inside iteration: {$index} (zero-based) and {$item} (raw value for simple arrays).
Conditionals
{if(award.costShareRequired, "Cost sharing required: " + (award.costShareAmount | money), "")}
Collection Functions
{inspections | count} → 12
{lineItems | sum:amount} → 1234.56
{lineItems | avg:unitPrice} → 29.99
Special Variables
| Variable | Description |
|---|---|
{$date} | Current date (locale-formatted) |
{$datetime} | Current date and time |
{$actor.name} | Person who triggered generation |
{$actor.email} | Actor’s email |
{$page} | Current page number (PDF only) |
{$pages} | Total page count (PDF only) |
{$params.name} | Parameter value (manual templates) |
27.6 Output Formats
The primary format for official documents. The layout block controls page formatting:
layout:
pageSize: letter
orientation: portrait
margins: { top: 1in, right: 1in, bottom: 1in, left: 1in }
header:
logo: "${INSTITUTION_LOGO_PATH}"
text: "Office of Sponsored Programs"
footer:
left: "Generated {$datetime}"
center: "Page {$page} of {$pages}"
right: "CONFIDENTIAL"
watermark:
text: "DRAFT"
opacity: 0.1
font:
family: "Helvetica"
size: 10
tableStyle:
headerBackground: "#f1f5f9"
borderColor: "#e2e8f0"
alternateRowBackground: "#f8fafc"
The Markdown body converts to formatted PDF: headers become styled sections, tables become bordered tables, bold/italic render with font weight/style, horizontal rules become page-width lines.
The PDF generator is a Kalos plugin implementing IDocumentRenderer.
DOCX
Word document output for editable templates:
format: docx
HTML
HTML output for browser rendering and email embedding:
format: html
Markdown
Raw Markdown output (interpolated, no format conversion):
format: markdown
27.7 Document Storage
Auto-Attachment
attachTo:
resource: Award
recordId: $record.id
field: awardNoticeDocId
On generation: store the file → create a documents table record → update the target record’s field with the document ID → record a history event.
Setting attachTo: none stores the document without linking it to a record.
Storage Backend
domain:
documentStorage:
provider: filesystem
basePath: "${DOCUMENT_STORAGE_PATH}"
structure: "{domain}/{resource}/{year}/{month}/"
For cloud storage:
documentStorage:
provider: s3
bucket: "${S3_DOCUMENT_BUCKET}"
region: "${AWS_REGION}"
prefix: "documents/"
Documents Table
| Column | Type | Description |
|---|---|---|
id | uuid | Document identifier |
templateName | string | Template definition that generated this document |
templateVersion | string | Domain version at generation time |
resource | string | Source resource name |
recordId | uuid | Source record ID |
filename | string | Generated filename |
format | string | Output format |
storagePath | string | Path in storage backend |
fileSize | integer | File size in bytes |
generatedBy | uuid | User who triggered generation |
generatedAt | datetime | Generation timestamp |
metadata | json | Parameters, data snapshot |
Document Versioning
Generated documents record which domain version produced them. Template changes in version 2.1.0 do not affect documents generated under version 2.0.0. The version trail is in the documents table.
27.8 Document API
GET /api/v1/{domain}/templates
— list all template definitions
GET /api/v1/{domain}/templates/{name}
— get template definition
POST /api/v1/{domain}/templates/{name}/generate
— manual generation with parameters (returns the document)
GET /api/v1/{domain}/documents
?resource=Award&recordId={id}&template=awardNotice
— list generated documents
GET /api/v1/{domain}/documents/{id}
— download generated document (binary)
GET /api/v1/{domain}/documents/{id}/metadata
— metadata without downloading
27.9 Notification with Document
notification:
channels: [email]
recipients:
field: piId
additional: [departmentChairId]
template:
subject: "Award Notice — {award.awardNumber}"
body: "Please find the attached notice of award for {award.title}."
attachDocument: true
When attachDocument: true, the generated file is attached to the notification email. This is how award letters, denial notices, and inspection certificates are stored on the record and delivered to the recipient in one declaration.
27.10 Integration Points
| Feature | Integration |
|---|---|
| History (§21) | Event-triggered generation; document generation produces history events |
| Notifications (§23) | Documents attached to notification emails via attachDocument |
| Schedules (§24) | Scheduled generation via action: template |
| Reports (§26) | Shared format system and data context assembly patterns |
| Workflows (§25) | Workflow steps can trigger document generation |
End of Chapter 27.
Next: Chapter 28 — Sheet Mode