Send email (via SMTP) from template
The service task "Send email (via SMTP) from template" sends an email through any SMTP server, like Send email (via SMTP). The difference: message.subject, message.html and message.text are Handlebars templates that are filled with the data from contentData before sending. Amounts, dates and lists are formatted with the Handlebars helpers instead of being assembled in the JSONata expression.
Technical name prio_sendEMailTemplateSMTP · Category in the "Select service task" dialog: Email
When is this service used?
- Quotes with figures: Musterfirma GmbH sends hardware quotes with total, number of items and validity date;
{{currency_eur …}}and{{toGermanDate …}}format the values. - Mailings from a record: A participant record is passed as
contentData, the template accesses it with{{participant.name}}. - Maintained template instead of code: The template is kept as a process value or in a record and is only referenced in the input.
Which email service?
| Service | Sender | Recipients |
|---|---|---|
| Send email (via SMTP) from template – this page – and Send email (via SMTP) | own mail server, any address the server allows | any |
| Send email from Handlebars template (Microsoft Graph) | Microsoft 365 account | any |
| Send email to external recipients (own domain) | own domain, approved for the account | any |
| Send email from template via Pantarey (internal) | benachrichtigung@pantarey.io |
users of the own Pantarey account only |
Prerequisites
As for Send email (via SMTP): an SMTP server reachable from the internet and the credentials as secrets. {{secret:NAME}} is replaced only in transport.auth.user and transport.auth.pass. In message.* the placeholder is not resolved; Handlebars would read {{secret:X}} there as an expression and render it empty or with an error.
Input
transport and all fields of message correspond to Send email (via SMTP). Differences:
| Field | Type | Required | Meaning |
|---|---|---|---|
message.subject |
string with Handlebars | no | Subject template; empty → No subject |
message.html, message.text |
string with Handlebars | no | Content templates; if both are missing → text No content |
contentData |
object | no | Data for the placeholders. If missing, all placeholders render empty |
Templates have to be text; an object in this place aborts with Vorlage muss Text sein (erhalten: …). Processing order: first disallowed fields are removed, then recipients are checked, then Handlebars renders, finally the fallbacks for subject and content apply.
Available helpers include dateTimeFormat, toGermanDate, currency_eur, numberFormat, multiply, add, round, toFixed, ifEquals, greaterThan, lessThan, replace, currentDate, addDaysToCurrentDate, month_name, year and qrCode; the complete list is in the Handlebars reference.
{
"transport": {
"host": "smtp.musterfirma-augsburg.de",
"port": 587,
"auth": { "user": "{{secret:SMTP_USER}}", "pass": "{{secret:SMTP_PASS}}" }
},
"message": {
"from": "vertrieb@musterfirma-augsburg.de",
"to": customer.email,
"subject": "Quote {{quote.number}} – hardware",
"html": "<p>Dear {{customer.contact}},</p><p>please find attached our quote for {{currency_eur quote.total}} covering {{quote.items.length}} items.</p><p>Valid until {{toGermanDate quote.validUntil}}.</p>",
"attachments": [ quote.pdf ]
},
"contentData": { "customer": customer, "quote": quote }
}
Output
{
"status": 200,
"response": {
"messageId": "<d4e5f6@musterfirma-augsburg.de>"
}
}
Identical to Send email (via SMTP): response.messageId is the server's identifier. The result is stored under the result path or is not stored with Discard result.
Errors and behavior
- Missing required fields and SMTP server errors abort the step, see Send email (via SMTP).
- Template errors (syntax errors, unclosed
{{, template not text) abort as well. - Every error throws: With an error boundary event the process continues there; without one the instance ends in error with a resume point and an error email to the process owner.
- Retries: Up to three attempts for transient errors (SMTP 450–452, timeout). A mail that was already accepted may then arrive twice. Template errors are not retried.
- Time limit: 900 seconds per call. The dry run does not send any mail.
Example
Musterfirma GmbH sends hardware quotes. The message contains customer (email, contact) and quote (number, total, items, validUntil, PDF as file object).
- Input expression as above.
contentDatapasses both objects; the template accesses them with{{customer.contact}}and{{quote.total}}. - Leave the result path empty and switch on Discard result; the message stays unchanged.
- Output
{ "status": 200, "response": { "messageId": "…" } }. - Next step: A timer waits 14 days, then a database step checks whether the quote was accepted.
Frequently asked questions
The subject should contain a literal {{. How?
Double curly braces that are not meant as Handlebars are escaped with a backslash: \{{example}}.
All placeholders are empty.
Then contentData is missing, or the paths in the template do not match the keys of the object. In the Process Explorer under Activities → In/Out you can see what the step received.
Where do secrets belong?
Only in transport.auth.user and transport.auth.pass. A {{secret:…}} in subject or content is read by Handlebars as an expression and is not replaced.
Next step
Handlebars helpers: all helpers for dates, currency, conditions and lists in templates.