Workflow: EmailTemplate Type
Introduction
An EmailTemplate workflow is a specialized workflow type that generates fully composed email content from business data.
Instead of sending the email directly, it returns the subject, body, and attachments so that other workflows or applications can send the email using standard email tasks or external systems.
EmailTemplate workflows are ideal when you need to:
- Standardize outbound communication (e.g., order confirmations, shipment status updates, invoices ready, POD notifications).
- Separate template logic from sending logic, so different channels (TMS UI, integrations, background workflows) can reuse the same email content.
- Build dynamic, data-driven emails using order, customer, shipment, and document data.
- Reuse existing document workflows as attachments (e.g., commercial invoice PDF, labels, packing lists).
EmailTemplate workflows use the standard workflow manifest structure, but with:
workflow.workflowType: "EmailTemplate"to mark the workflow as an EmailTemplate workflow.- A fixed output contract that returns:
subject: Email subject line.body: Email body as HTML/text.attachments: Array of attachment definitions compatible with theEmail/Send@1task.
YAML Structure
An EmailTemplate workflow is defined using the standard workflow manifest with a specialized workflowType and required outputs.
High-level structure:
workflow:
workflowId: "00000000-0000-0000-0000-000000000000"
name: "Emails / Order Confirmation"
isActive: true
workflowType: "EmailTemplate"
executionMode: "Sync" # EmailTemplate workflows should execute synchronously
version: "1.0"
tags:
- "email"
- "template"
- "order"
inputs:
- name: "orderId"
type: "number"
props:
required: true
visible: false
outputs:
- name: "subject"
mapping: "subject"
- name: "body"
mapping: "body"
- name: "attachments"
mapping: "attachments"
activities:
- name: "data"
steps: []
- name: "composeEmail"
steps: []
Key structural notes:
workflow.workflowTypemust be set to"EmailTemplate".executionModeshould be"Sync", because email content is usually needed immediately by the caller.- Inputs typically include identifiers such as
orderId,customerId, or other domain keys. - Outputs must include
subject,body, andattachments. - Activities usually follow this pattern:
- Load domain data (e.g.,
Order,Customer). - Build reusable metadata (optional).
- Compose email subject and body.
- Build attachment definitions (referencing documents or workflows).
- Load domain data (e.g.,
Attribute Description
Workflow-Level Attributes
| Attribute | Type | Required | Description | Example |
|---|---|---|---|---|
workflow.workflowId | string | Yes | Unique identifier of the workflow definition. | "2e28201d-704e-40b1-8568-7a87d198e255" |
workflow.name | string | Yes | Display name of the workflow. | "Emails / Order Confirmation" |
workflow.workflowType | string | Yes | Must be set to "Email Template" for this workflow type. | "Email Template" |
workflow.executionMode | string | Yes | Execution mode. Email Template workflows should use "Sync". | "Sync" |
workflow.version | string | Yes | Version of the workflow definition file. | "1.0" |
workflow.tags[] | array | Recommended | Tags for categorization and discovery (e.g., email, order, template). | ["email","order","template"] |
workflow.description | string | No | Human-readable description of what the template does. | "Order confirmation email template" |
For all other workflow-level fields (e.g.,
logLevel,concurrency,enableAudit), see Workflow Manifest YAML.
Inputs
EmailTemplate workflows are typically driven by an order-centric input model.
| Attribute | Type | Required | Description | Example |
|---|---|---|---|---|
inputs[].name | string | Yes | Name of the input parameter. | "orderId" |
inputs[].type | string | Yes | Data type of the input (e.g., number, text, domain-specific types). | "number" |
inputs[].props.* | object | No | Standard input UI and validation properties. | See manifest |
Typical input:
| Name | Type | Required | Description |
|---|---|---|---|
orderId | number | Yes | Unique identifier of the order. |
Outputs (Email Contract)
EmailTemplate workflows must return the following outputs:
| Attribute | Type | Required | Description | Example |
|---|---|---|---|---|
outputs.subject | string | Yes | Email subject line after all templating and data mapping. | "Order #12345 confirmed" |
outputs.body | object | Yes | Email body object, compatible with Email/Send@1 (html and/or text fields). | { html: "<h1>...</h1>", text: "..." } |
outputs.attachments | array | Yes | Array of attachment definitions compatible with Email/Send@1. May be empty. | [{ name: "Invoice.pdf", documentWorkflowId: "..." }] |
subject
- Type:
string - Required: Yes
- Description: The final subject line for the email.
- Example:
"Order #12345 confirmation – ACME Logistics"
body
Email body should follow the structure used by the Email task:
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
body.html | string | Recommended | Rich HTML content of the email body. | "<h1>Order...</h1>" |
body.text | string | Optional | Plain-text version of the email body (fallback). | "Order 12345 confirmed..." |
html: Preferred for rich formatting; rendered in most clients.text: Provides a simple fallback for plain-text clients or logging.
attachments[]
Attachment objects must be compatible with Email Task attachments:
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
attachments[].name | string | No | Display name of the attachment file. | "CommercialInvoice.pdf" |
attachments[].attachmentId | number | No | Reference to an existing stored attachment by ID. | 101 |
attachments[].orderDocumentId | number | No | Reference to an order document record. | 102 |
attachments[].documentWorkflowId | string | No | Document workflow ID used to generate an attachment on demand. | "4afb7c5f-9cf9-46a2-973f-2df763e935d1" |
attachments[].documentWorkflowVariables | object | No | Variables passed to the document workflow when generating the attachment. | { orderId: 12345 } |
attachments[].content | string | No | Base64-encoded file content (used when attaching ad-hoc content). | "VGhpcyBpcyBhIHRlc3QgYXR0YWNobWVudA==" |
At least one of attachmentId, orderDocumentId, documentWorkflowId, or content should be provided for each attachment entry.
Triggers
EmailTemplate workflows are usually triggered manually or from other workflows:
| Attribute | Type | Required | Description | Example |
|---|---|---|---|---|
triggers[].type | string | Yes | Trigger type (Manual, Entity, Schedule). | "Manual" |
triggers[].entityName | string | Recommended for Manual/Entity | Entity where the action is exposed. | "Order" |
triggers[].displayName | string | Recommended | Label shown in the UI. | "Send Email" |
Examples
Example: Order Confirmation Email Template
This example workflow:
- Loads order and customer data.
- Builds a metadata object.
- Composes the subject and body using metadata.
- Builds attachments using document workflows and order documents.
workflow:
name: "Emails / Order Confirmation"
workflowId: "2e28201d-704e-40b1-8568-7a87d198e255"
isActive: true
workflowType: "EmailTemplate"
executionMode: "Sync" # EmailTemplate workflows should execute synchronously
version: "1.0"
tags:
- "email"
- "order"
- "template"
variables:
- name: "organizationId"
value: 123
inputs:
- name: "orderId"
type: "number"
props:
required: true
visible: false
outputs:
- name: "subject"
mapping: "subject"
- name: "body"
mapping: "body"
- name: "attachments"
mapping: "attachments"
triggers:
- type: "Manual"
displayName: "Generate Order Confirmation Email"
entityName: "Order"
activities:
- name: data
steps:
- task: "Query/GraphQL"
name: "getOrder"
inputs:
query: |
query($orderId: Int!, $organizationId: Int!) {
order(orderId: $orderId, organizationId: $organizationId) {
orderId
orderNumber
orderDate
totalAmount
customer {
customerId
name
email
}
}
}
variables:
orderId: "{{ orderId }}"
organizationId: "{{ organizationId }}"
outputs:
- name: "order"
mapping: "order"
- name: metadata
steps:
- task: "Utilities/SetVariable@1"
name: "buildMetadata"
inputs:
name: "meta"
value:
orderId: "{{ data.getOrder.order.orderId }}"
orderNumber: "{{ data.getOrder.order.orderNumber }}"
orderDate: "{{ data.getOrder.order.orderDate }}"
totalAmount: "{{ data.getOrder.order.totalAmount }}"
customerId: "{{ data.getOrder.order.customer.customerId }}"
customerName: "{{ data.getOrder.order.customer.name }}"
customerEmail: "{{ data.getOrder.order.customer.email }}"
- name: composeEmail
steps:
# Subject
- task: "Utilities/SetVariable@1"
name: "buildSubject"
inputs:
name: "subject"
value: "Order {{ meta.orderNumber }} confirmation – Thank you for your business"
# Body (HTML + Text)
- task: "Utilities/SetVariable@1"
name: "buildBody"
inputs:
name: "body"
value:
html: |
<h1>Order Confirmation</h1>
<p>Dear {{ meta.customerName }},</p>
<p>Thank you for your order <strong>#{{ meta.orderNumber }}</strong> placed on {{ meta.orderDate }}.</p>
<p>Total Amount: <strong>{{ meta.totalAmount }}</strong></p>
<p>You can find your documents attached to this email.</p>
<p>Best regards,<br/>CargoXplorer TMS</p>
text: |
Dear {{ meta.customerName }},
Thank you for your order #{{ meta.orderNumber }} placed on {{ meta.orderDate }}.
Total Amount: {{ meta.totalAmount }}
Your documents are attached to this email.
Best regards,
CargoXplorer TMS
# Attachments compatible with Email/Send@1
- task: "Utilities/SetVariable@1"
name: "buildAttachments"
inputs:
name: "attachments"
value:
- name: "CommercialInvoice_{{ meta.orderNumber }}.pdf"
documentWorkflowId: "4afb7c5f-9cf9-46a2-973f-2df763e935d1"
documentWorkflowVariables:
orderId: "{{ meta.orderId }}"
organizationId: "{{ organizationId }}"
- name: "Label_{{ meta.orderNumber }}.pdf"
orderDocumentId: 102
The caller (another workflow or an external system) can then:
- Execute this Email Template workflow with
orderId. - Use the returned
subject,body, andattachmentsas inputs toEmail/Send@1(or equivalent email sending mechanism).
Best Practices
- Keep logic pure and idempotent:
- EmailTemplate workflows should not send emails themselves.
- They should only compute and return subject, body, and attachments.
- Align outputs with
Email/Send@1:- Ensure
bodymatches the expected{ html, text }structure. - Ensure
attachmentsentries use the same fields as the Email task.
- Ensure
- Use metadata for reuse:
- Build a
metaobject containing all derived values (names, totals, formatted dates). - Reuse
metaacross subject, body, and attachments to avoid duplication.
- Build a
- Prefer
Syncexecution:- Use
executionMode: "Sync"so callers can immediately receive the email content. - Avoid long-running operations inside Email Template workflows.
- Use
- Reuse Document workflows for attachments:
- Use
documentWorkflowIdanddocumentWorkflowVariablesto attach generated documents. - Keep document generation logic in Document workflows, not in the EmailTemplate workflow.
- Use
- Minimize PII exposure:
- Include only the personal data required to render the email.
- Avoid persisting sensitive metadata unless necessary.
- Localization and branding:
- Use metadata to inject localized labels, currency formats, and branding.
- Consider separate templates or conditionals for different divisions or languages.