How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

Send an email when a record is created

A job on the record's create event, or a form's Completed event, with one Send mail action whose body is a template in your repository. Read a submission's answers through data, escape them, and reply-to the sender.

Context

Context. The most common job: something is created, and a person should hear about it. Here a contact form is submitted and the enquiries team gets an email listing every answer, with reply-to set to the person who wrote in so a reply goes straight back. The same shape works for any record: a new page for the content team, a new member for the membership officer.

Steps

  1. In Customization › Orchestration, create a job named Form: contact → email enquiries, limited to the site, asynchronous, batch persistence always.
  2. Add the trigger: entity event, form submissions, method Completed, narrowed to the contact form by its record id. Completed fires once, when the submission is finished; CreateRecord can fire before the visitor has finished a multi-step form. For any other record, use CreateRecord on its entity.
  3. Put the email body in the repository at templates/hooks/emails/contact-enquiry.html (below) and deploy it.
  4. Add a Send mail action email-enquiries: to {$siteData->get('enquiries-to')}, reply-to {$event->getRecord()->data['email']}, subject Website enquiry from {$event->getRecord()->data['name']}, body from the template file.
  5. Set enquiries-to in site data on each environment: your own address on development, the team's inbox on production.
  6. Publish the job, submit the form once, and open the batch in Batch Logs: Send mail should say Mail queued.

Code

Template code
{* templates/hooks/emails/contact-enquiry.html *}
{$answers = $event->getRecord()->data}
<!DOCTYPE html>
<html><body style="font-family:Arial,sans-serif;font-size:14px;color:#333333;">
<p>A new enquiry was sent from the contact form.</p>
<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">
{foreach $answers as $field => $value}
  <tr>
    <td style="border-bottom:1px solid #e4e4e4;font-weight:bold;">{$field|escape}</td>
    <td style="border-bottom:1px solid #e4e4e4;">{if is_array($value)}{implode(', ', $value)|escape}{else}{$value|escape|nl2br}{/if}</td>
  </tr>
{/foreach}
</table>
<p style="color:#777777;font-size:12px;">Submission {$event->getRecordId()}. Reply to this email to answer the sender.</p>
</body></html>
{# templates/hooks/emails/contact-enquiry.html #}
{% set answers = event.getRecord().data %}
<!DOCTYPE html>
<html><body style="font-family:Arial,sans-serif;font-size:14px;color:#333333;">
<p>A new enquiry was sent from the contact form.</p>
<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">
{% for field, value in answers %}
  <tr>
    <td style="border-bottom:1px solid #e4e4e4;font-weight:bold;">{{ field }}</td>
    <td style="border-bottom:1px solid #e4e4e4;">{{ value is iterable ? value|join(', ') : value|nl2br }}</td>
  </tr>
{% endfor %}
</table>
<p style="color:#777777;font-size:12px;">Submission {{ event.getRecordId() }}. Reply to this email to answer the sender.</p>
</body></html>

The answers are data, keyed by each field's name in the form; a checkbox group or a file field gives a list. Escape every value: it is whatever a visitor typed.

Why this and not that

Not getAreaText() on the submission: a job's records are API models, and that method does not exist on them. Not the visitor's address in From: mail from your domain claiming to be from someone else's fails the receiving server's checks, which is what reply-to is for. And make the form's email field required and validated: an address that is not a valid email fails the Send mail action, and the team never hears about the enquiry. The action succeeding means the email was queued, not delivered; Email from jobs covers the rest of the path, and how to test without mailing a customer.

Go deeper

Was this page helpful?

Your answer goes to the docs team.