Custom Pipeline Email Notifications
Tetra Data Pipelines can send email notifications when a workflow completes or fails. With custom templates enabled, you can replace the default email body with your own HTML content using Handlebars syntax ({{variable}}).
Custom templates are configured for each pipeline in the Pipeline Editor under the Notifications section. For instructions, see Create Custom Pipeline Email Notifications.
You can define separate body and subject templates for success and failure notifications. All templates use the same Handlebars variables.
NOTE
Custom pipeline email notifications are available in TDP v4.4.2 and higher as part of a limited availability release and must be activated in coordination with TetraScience. To start using custom notifications, contact your customer account leader.
Pipeline Success Custom Email Notification Template
<h2>Workflow Completed Successfully</h2>
<p><strong>Organization:</strong> {{org}}</p>
<p><strong>Pipeline:</strong> <a href="{{workflow.pipeline.url}}">{{workflow.pipeline.name}}</a></p>
<p><strong>Workflow ID:</strong> {{workflow.id}}</p>
<p><a href="{{workflow.url}}">View Workflow Details →</a></p>
{{#if workflow.trigger.file.name}}
<p><strong>File Processed:</strong> {{workflow.trigger.file.name}}</p>
{{/if}}
{{#if workflow.trigger.file.labels}}
<h3>File Labels</h3>
<ul>
{{#each workflow.trigger.file.labels}}
<li><strong>{{@key}}:</strong> {{this}}</li>
{{/each}}
</ul>
{{/if}}
Pipeline Failure Custom Email Notification Template
<h2>Workflow Failed</h2>
<p><strong>Organization:</strong> {{org}}</p>
<p><strong>Pipeline:</strong> <a href="{{workflow.pipeline.url}}">{{workflow.pipeline.name}}</a></p>
<p><strong>Workflow ID:</strong> {{workflow.id}}</p>
{{#if workflow.trigger.file.name}}
<p><strong>File:</strong> {{workflow.trigger.file.name}}</p>
{{/if}}
{{#if workflow.error}}
<h3>Error</h3>
<pre style="background: #f5f5f5; padding: 10px; overflow-x: auto;">{{workflow.error}}</pre>
{{/if}}
{{#each workflow.tasks}}
{{#if this.isFailed}}
<h3>Failed Task: {{this.step}}</h3>
{{#if this.error}}
<p><strong>Error:</strong> {{this.error}}</p>
{{/if}}
{{/if}}
{{/each}}
{{#if workflow.trigger.file.labels}}
<h3>File Labels</h3>
<ul>
{{#each workflow.trigger.file.labels}}
<li><strong>{{@key}}:</strong> {{this}}</li>
{{/each}}
</ul>
{{/if}}
<p><a href="{{workflow.url}}">View Workflow Details →</a></p>
IMPORTANT
Deprecated fields:
workflow.logs(v2 protocol-only, rarely populated) andtask[*].output(not yet supported) are documented for completeness but not recommended for use. Useworkflow.errorandtask[*].errorinstead.
Important Considerations
- Subject lines: Custom subject templates use the same Handlebars variables as the body. Subjects are rendered as plain text only. any HTML tags are automatically stripped. If no custom subject is defined, the platform generates a default.
- Sender and recipients: Sender address and recipient list are controlled by pipeline notification settings in the TDP UI, not the template.
- Fallback behavior: If a custom template fails to compile (syntax error), the email will fall back to the default TDP template.
- Inline styles only: Email clients (Gmail, Outlook, Apple Mail) strip
<style>blocks and ignore external stylesheets. All CSS must be applied as inlinestyleattributes — e.g.,<pre style="background: #f5f5f5; padding: 10px;">. The examples in this document follow this pattern. - Available helpers: Only built-in Handlebars helpers are available:
#if,#each,#with,lookup. Custom helpers likejson,stringify, etc. are not registered and will cause template compilation to fail silently.
Available Template Variables
The following variables are available inside your Handlebars templates at render time, organized by context:
Root-Level Variables
| Variable | Type | Description |
|---|---|---|
environment | string | Deployment name and environment (e.g., "prod - prod") |
domain | string | Platform domain (e.g., "tetrascience.com") |
org | string | Organization slug (e.g., "my-org") |
Workflow Context (workflow.*)
workflow.*)| Variable | Type | Description |
|---|---|---|
workflow.id | string | Unique workflow execution ID |
workflow.status | string | "completed" or "failed" |
workflow.isSuccess | boolean | true if workflow completed successfully |
workflow.isFailed | boolean | true if workflow failed |
workflow.error | string | Error message (if failed) |
workflow.url | string | Direct URL to view workflow details in TDP |
workflow.logs | array | Deprecated. Array of log entries (v2 protocols only, rarely populated). Not recommended for use. |
Pipeline Context (workflow.pipeline.*)
workflow.pipeline.*)| Variable | Type | Description |
|---|---|---|
workflow.pipeline.id | string | Pipeline ID |
workflow.pipeline.name | string | Pipeline name as configured in TDP |
workflow.pipeline.url | string | Direct URL to edit the pipeline in TDP |
workflow.pipeline.revision | number | Pipeline configuration revision number |
workflow.pipeline.artifact.namespace | string | Protocol namespace (e.g., "common") |
workflow.pipeline.artifact.slug | string | Protocol slug (e.g., "python-exec-benchling") |
workflow.pipeline.artifact.version | string | Protocol version (e.g., "v1.0.0") |
workflow.pipeline.artifact.type | string | Artifact type (e.g., "protocol") |
File/Trigger Context (workflow.trigger.file.*)
workflow.trigger.file.*)| Variable | Type | Description |
|---|---|---|
workflow.trigger.file | object | Present for file-triggered workflows. May be absent for other trigger types — guard with {{#if workflow.trigger.file}} |
workflow.trigger.file.name | string | Filename that triggered the workflow, or "scheduled" for scheduled pipelines |
workflow.trigger.file.metadata | object | Custom metadata tags from the file (object, not JSON string—use dot notation: {{workflow.trigger.file.metadata.project}}) |
workflow.trigger.file.tags | array | Array of custom tags |
workflow.trigger.file.labels | object | Object mapping label names to label values (like {"sample_id": "S0123", "instrument": "INST-00501"}) |
IMPORTANT
Labels are a flat object, not an array of objects with 'name', 'value' properties
To iterate over labels, use thehandlebars #eachkeyword. For example:
{{#each workflow.trigger.file.labels}}
{{@key}}: {{this}}
{{/each}}
Task Context (workflow.tasks.*)
workflow.tasks.*)| Variable | Type | Description |
|---|---|---|
workflow.tasks | array | Array of task execution objects |
workflow.tasks[*].id | string | Task ID |
workflow.tasks[*].step | string | Task script slug/name |
workflow.tasks[*].status | string | Task status: "completed", "failed", "skipped", etc. |
workflow.tasks[*].isSuccess | boolean | true if task completed successfully |
workflow.tasks[*].isFailed | boolean | true if task failed |
workflow.tasks[*].isSkipped | boolean | true if task was skipped |
workflow.tasks[*].input | object | Task input data |
workflow.tasks[*].output | object | Placeholder field. Almost always empty ({}). |
workflow.tasks[*].error | any | Error details if the task failed. Shape varies by task type and failure mode. Use the debug blocks to inspect the actual structure for your pipeline |
workflow.tasks[*].script.namespace | string | Script namespace |
workflow.tasks[*].script.slug | string | Script slug |
workflow.tasks[*].script.version | string | Script version |
workflow.tasks[*].script.function | string | Function name |
workflow.tasks[*].memoryInMB | number | Memory allocated to task |
workflow.tasks[*].events | array | Array of task lifecycle events with status and at (timestamp) |
Custom Subject Lines
Custom subject templates use the same Handlebars variables as body templates. Subjects are rendered as plain text only. Any HTML tags are automatically stripped.
Example Success Subject
[{{org}}] {{workflow.pipeline.name}} — {{workflow.trigger.file.name}}
Example Failure Subject
[{{org}}] {{workflow.pipeline.name}} failed — {{workflow.error}}
If no custom subject is defined, the platform generates a default:
- Success:
[org] Workflow Successful - protocol-slug - Failure:
[org] Workflow Failed - protocol-slug
Debugging Your Template
If your template isn't rendering as expected, you can inspect the actual data structure by adding debug blocks to your template. The Handlebars renderer automatically converts all objects to JSON.
Debug Blocks
Add these sections anywhere in your template to inspect the data:
<!-- Debug: Print the entire workflow object -->
<div style="background: #f5f5f5; padding: 10px; margin: 20px 0; border: 2px solid red;">
<h3>DEBUG: Full Workflow Context</h3>
<pre style="overflow: auto; font-size: 11px; white-space: pre-wrap;">{{workflow}}</pre>
</div>
<!-- Debug: Print file/trigger data -->
<div style="background: #f5f5f5; padding: 10px; margin: 20px 0; border: 2px solid red;">
<h3>DEBUG: File/Trigger Data</h3>
<pre style="overflow: auto; font-size: 11px; white-space: pre-wrap;">{{workflow.trigger.file}}</pre>
</div>
<!-- Debug: Print labels specifically -->
<div style="background: #f5f5f5; padding: 10px; margin: 20px 0; border: 2px solid red;">
<h3>DEBUG: File Labels</h3>
<pre style="overflow: auto; font-size: 11px; white-space: pre-wrap;">{{workflow.trigger.file.labels}}</pre>
</div>
<!-- Debug: Print all tasks -->
<div style="background: #f5f5f5; padding: 10px; margin: 20px 0; border: 2px solid red;">
<h3>DEBUG: All Tasks</h3>
<pre style="overflow: auto; font-size: 11px; white-space: pre-wrap;">{{workflow.tasks}}</pre>
</div>
When your template is rendered and sent, these debug sections will display the actual JSON structure of the data, showing you exactly what properties are available. This is especially useful in the following situations:
- Variables are rendering as empty or undefined
- You're unsure about the structure of nested objects
- You want to verify that data is being passed to the template
Remember to remove these debug blocks before putting your template into production!
Documentation Feedback
Do you have questions about our documentation or suggestions for how we can improve it? Start a discussion in TetraConnect Hub. For access, see Access the TetraConnect Hub.
NOTE
Feedback isn't part of the official TetraScience product documentation. TetraScience doesn't warrant or make any guarantees about the feedback provided, including its accuracy, relevance, or reliability. All feedback is subject to the terms set forth in the TetraConnect Hub Community Guidelines.
Updated about 11 hours ago
