Placeholder Reference

How placeholders work in email templates and what every workflow placeholder means.

Table of Contents

  1. What a placeholder is
  2. Two kinds of placeholders
  3. Placeholder syntax
  4. Placeholder definition fields
  5. Data types
  6. Allowed filters
  7. Missing values, defaults, and required placeholders
  8. Document placeholders (document.*)
  9. Workflow placeholders (workflow.*)
  10. Runtime notification placeholders
  11. How workflow notifications receive values
  12. Examples
  13. A note on testing

What a placeholder is

A placeholder is a named value that is filled in when the email is rendered. In the template it looks like:

text
Hello {{ recipient_name }}, your request {{ document.number }} is ready.

At send time, the system replaces each token with the real value. Placeholders let one template serve many records and recipients.


Two kinds of placeholders

Kind Example Declared by Used by
Template placeholder {{ recipient_name }} You, in the template's placeholder definitions Any email template
Workflow placeholder {{ workflow.approvers }}, {{ document.number }} You still declare them, but the workflow data source supplies the names and samples Email templates linked to a workflow

Workflow placeholders are not magic tokens that bypass validation. In Email Studio, every token must be declared. When you click a workflow field in the Data source panel, the designer inserts the token and declares it for you. If you type {{ workflow.approvers }} manually, you must also add a placeholder definition named workflow.approvers.


Placeholder syntax

Rule Detail
Token form {{ name }} or {{ name|filter }}
Name form Letters, numbers, and underscores; dots separate path segments.
Pattern ^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*$
Underscore rule No segment may start with _.
Uniqueness Each name may be declared only once.
Prefix rule A name cannot also be a path prefix. Do not declare both document and document.number.
Django tags {% ... %} and {# ... #} are rejected.
Unknown tokens An undeclared token is a validation error, not a blank value.
Case Names are case-sensitive. Recipient_Name and recipient_name are different.

Placeholder definition fields

Each declared placeholder has these attributes.

Field Required Meaning
name Yes The token name, without braces. Must follow the syntax rules above.
description No Human-readable note explaining what the value contains. Shown in the designer.
data_type Yes How the value is validated and formatted. Defaults to text. See Data types.
required Yes When true, the email will not render unless a value is supplied. A required placeholder cannot define a default.
sample No Example value used by preview and by the workflow data source panel.
default No Value used when an optional placeholder has no value. Ignored for required placeholders.
allow_html No When true, the value is treated as trusted HTML. Only valid with data_type: html. Such placeholders may only be used in the HTML body, never the subject or plain-text body.
format No Formatting hint. For number, integer, and decimal, the number of decimal places. For date and datetime, a Django date-format string such as N j, Y.

Data types

Data type What it expects Behavior
text Any text Inserted as-is; escaped in the HTML body.
number A numeric value Converted to a decimal. A format of 2 renders two decimal places.
integer A whole number Converted to an integer. A format of 0 (or any digits) renders grouped digits.
decimal A decimal value Converted to a decimal; format sets decimal places.
date A date Accepts a date or a parsable date string. format controls the output.
datetime A date and time Accepts a datetime or a parsable datetime string. format controls the output.
boolean True/false Accepts booleans and true/false, yes/no, 1/0, on/off; renders Yes or No.
email An email address Validated as an email address.
url A URL Validated as an absolute URL. Use this for clickable links.
html Trusted HTML Sanitized, then inserted unescaped. Must also set allow_html, and may only be used in the HTML body.

Use url for links. A plain text value containing a URL is not validated as a URL, and a URL placeholder is validated after rendering to prevent unsafe schemes.


Allowed filters

Filters are optional and are written with a pipe: {{ name|lower }}. Only these filters are allowed:

Filter Effect Example
upper Uppercase text {{ status|upper }} → APPROVED
lower Lowercase text {{ status|lower }} → approved
title Title Case {{ state_name|title }} → Pending Review
capfirst Capitalize the first character {{ reason|capfirst }}
date Format a date {{ submitted_at|date:"N j, Y" }}
time Format a time {{ submitted_at|time:"H:i" }}
floatformat Format a decimal {{ amount|floatformat:2 }}
default Use a fallback when the value is falsey {{ comment|default:"No comment" }}
default_if_none Use a fallback only when the value is null {{ comment|default_if_none:"-" }}

Any other filter is rejected at validation time.


Missing values, defaults, and required placeholders

The renderer follows these rules:

Situation Result
Value is present The value is used.
Optional placeholder has no value, and a default exists The default is used, and a warning is shown.
Optional placeholder has no value and no default An empty string is used, and a warning is shown.
Required placeholder has no value An error is raised; the email is not sent.
Required placeholder defines a default Invalid definition; required placeholders cannot have defaults.

Warnings do not block sending. Errors do.


Document placeholders (document.*)

document.* placeholders read fields from the business document attached to the workflow, such as an invoice, purchase order, or request.

Single fields

text
{{ document.number }}
{{ document.status }}
{{ document.due_date }}

Any concrete field on the workflow's document model can be used, with these exclusions:

  • Sensitive or account fields: password, is_superuser, is_staff, groups, user_permissions, last_login, date_joined.
  • Binary, file, and image fields are not exposed.

When the workflow configuration declares relationships, you can read a field on a related record:

text
{{ document.vendor.name }}
{{ document.customer.email }}

document.<relation>.<field> supports the relations configured for that workflow. Relations to the framework user model, Django groups, and Django permissions are excluded from automatic discovery.

Formatting behavior

  • Choice fields render their human-readable label, not the stored value.
  • Booleans render Yes or No.
  • Dates and datetimes render using the project's date formats, or the placeholder's format string.
  • Many-to-many values render as a comma-separated list.
  • Missing or unset values render as an empty string.

document_label

{{ document_label }} is the display name of the whole document, for example the formatted record title. Use it in a sentence such as "Regarding {{ document_label }}…".


Workflow placeholders (workflow.*)

These values describe the workflow itself. The designer offers:

  • The core catalog below, always available for a workflow data source.
  • Additional context fields built from the live workflow instance.
  • Document fields from the selected workflow's document model.

Core workflow catalog

Placeholder Meaning Typical value
{{ workflow.approvers }} All approvers assigned to the current work item. When an approval request is sent, this is set to the recipient of that message. Jane Doe, Alex Kim
{{ workflow.work_item_owner }} The user or users who currently own the work item. When an approval request is sent, this is set to the recipient. Jane Doe
{{ workflow.workflow_originator }} The user who initiated the workflow. Sam Rivera
{{ workflow.last_action_user }} The user who performed the most recent action on the workflow. Alex Kim
{{ workflow.last_note }} The most recent non-system note or approver comment. Please attach the invoice.
{{ workflow.link_to_web }} Absolute web link to the workflow document. https://erp.example.com/...

Additional workflow context fields

These are produced from the workflow instance and are offered by the workflow data source.

Placeholder Meaning
{{ workflow.all_approvers }} All approvers on the workflow. Falls back to workflow.approvers when no separate value exists.
{{ workflow.all_work_item_owners }} All work-item owners on the workflow. Falls back to workflow.work_item_owner.
{{ workflow.originator }} Alias of the originator's display name.
{{ workflow.originator_name }} Alias of the originator's display name.
{{ workflow.initiated_by }} Alias of the originator's display name.
{{ workflow.originator_email }} The originator's email address.
{{ workflow.initiated_by_email }} The originator's email address (alias).
{{ workflow.workflow_name }} The workflow type name, for example the registered workflow name.
{{ workflow.workflow_template }} The name of the workflow template.
{{ workflow.workflow_id }} The unique ID of the workflow instance.
{{ workflow.current_step }} The name of the step currently being processed. For a state-based workflow this reflects the active state step.
{{ workflow.status }} The human-readable workflow status, for example In Progress or Approved.
{{ workflow.items }} The document line items, joined into one string. Up to the first 20 lines are included.
{{ workflow.item_count }} The number of document line items.
{{ workflow.links }} Alias of the workflow web link.
{{ workflow.recipient_name }} The recipient's display name. In authoring/preview this is a sample name; at send time it is the actual recipient.
{{ workflow.recipient_email }} The recipient's email address. In authoring/preview this is a sample address; at send time it is the actual recipient.

On an approval request, the email channel sets workflow.approvers and workflow.work_item_owner to the recipient of that specific message, so the email addresses the person being asked to act.

Workflow aliases at a glance

Several names point at the same underlying value. Use whichever reads best in the email.

Underlying value Equivalent tokens
Originator display name workflow.workflow_originator, workflow.originator, workflow.originator_name, workflow.initiated_by
Originator email workflow.originator_email, workflow.initiated_by_email
Workflow web link workflow.link_to_web, workflow.links
All approvers workflow.approvers, workflow.all_approvers
Work-item owner(s) workflow.work_item_owner, workflow.all_work_item_owners

Runtime notification placeholders

These top-level values are supplied when a notification is sent. They are available as data-source fields for workflow templates and can be declared on the template.

Placeholder Meaning
{{ recipient_name }} Display name of the person receiving the email.
{{ recipient_email }} Email address of the recipient.
{{ notification_title }} Title of the notification, usually also used as the subject.
{{ notification_message }} Main notification message.
{{ notification_type }} Machine-readable notification type, for example approval_request or completion.
{{ action_url }} Link to the related record or action. Falls back to the workflow link when not supplied.
{{ document_label }} Display label of the related document.
{{ status }} Workflow status value.
{{ submitted_at }} Timestamp when the workflow was submitted.
{{ initiated_by }} Display name of the initiator.
{{ current_step }} Name of the current step.
{{ workflow_template }} Name of the workflow template.
{{ comment }} Comment attached to the action or request. Empty when none.
{{ step_name }} Name of the current step. Empty when not applicable.
{{ state_name }} Name of the current state in a state-based workflow. Empty when not applicable.
{{ state_code }} Code of the current state. Empty when not applicable.
{{ branch_id }} Identifier of the active parallel branch, when the workflow uses Fork/Join.
{{ reassigned_from }} Display name of the previous assignee on a reassignment. Empty when not applicable.
{{ reason }} Reason supplied for the action, rejection, or reassignment. Empty when none.

Notification types

These are the standard notification types a workflow can send:

submission, approval_request, approval_request_originator, approval_approved, rejection, changes_requested, completion, state_assignment, join_ready, state_completion, reassignment, delegation_expired.

Use {{ notification_type }} when one template serves several notification types, for example to vary wording.


How workflow notifications receive values

When a workflow sends an email:

  1. The workflow resolves the template code for the notification type from the step, the workflow template map, or the settings fallback.
  2. Enezaframework builds the email context from the live workflow instance and current step. It includes the workflow.* object, the document.* values, and the runtime top-level values.
  3. The email channel adds recipient details (recipient_name, recipient_email), the notification title and message, and a fallback action_url.
  4. The active published version is rendered with that context, placeholders are validated and coerced, and the email is queued.
  5. If a declaration expects a name the workflow does not supply, the send fails validation. The workflow designer warns about this when you map the template, but preview and test send are the reliable checks.

If no email template is selected for a workflow notification, the email channel is skipped and the delivery is logged as failed with No email template selected.

Placeholder resolution in notification text

The workflow's in-app notification text uses a separate lightweight renderer. It resolves:

  • workflow.* tokens from the current workflow and step.
  • document.* tokens for single fields and one related level.
  • Unknown tokens are left unchanged rather than blanked.

Email templates use the full template engine described on this page: every token must be declared, and undeclared tokens are validation errors.


Examples

Approval request

text
Subject: Approval needed for {{ document.number }}

Hello {{ recipient_name }},

{{ workflow.workflow_originator }} submitted {{ document_label }}
for your approval at the {{ workflow.current_step }} step.

Current status: {{ workflow.status }}
Last note: {{ workflow.last_note }}

Review the item: {{ workflow.link_to_web }}

Rejection notice

text
Subject: {{ document_label }} was rejected

Hello {{ workflow.workflow_originator }},

Your request was rejected by {{ workflow.last_action_user }}.
Reason: {{ reason }}

Open the record: {{ action_url }}

Generic notification

text
Subject: {{ notification_title }}

Hello {{ recipient_name }},

{{ notification_message }}

Open the item: {{ action_url }}

Regards,
{{ company_name }}

For the generic example, declare notification_title, notification_message, recipient_name, action_url, and company_name (all text, except action_url as url).


A note on testing

Per project rules, the developer or agent does not run or claim test results. The user is solely responsible for all testing and validation of the output described in this guide.