Emails
Send a notification email every time a shipment changes to a given status. The plugin ships no default emails; you build each one.
Audience: store admins setting up customer and internal notifications.
Where to configure
Shipments -> Settings -> Emails. Admin-only by default (grant shipments-manageEmails to give others access).
Per email, you configure:
- Basic fields: name, subject, recipient type, To / BCC / CC / Reply-To, HTML template path, optional plain-text template path, language, enabled toggle.
- Transition triggers: which status changes send this email.
The template path field autocompletes against your site templates.
Recipients
- Customer, sends to the order’s customer email. Renders in the order’s language (or the site default).
- Custom, sends to whatever addresses you put in To. The field is Twig-rendered, so you can do dynamic addressing:
Separate multiple addresses with commas, semicolons, or whitespace.{% if shipment.carrier == 'UPS' %}ups-ops@your-store.example{% else %}fedex-ops@your-store.example{% endif %}
Templates
Templates render with shipment and order available, plus the to/from codes, the user who made the change, and any note the admin left. Authoring or customizing templates is developer work; see the email templates dev guide for the full variable list and a starter template.
Transition triggers
This controls “when does this send.” Scroll to the bottom of the email edit page: a checkbox per status. Check one or more. The email sends when a shipment changes into any checked status.
Uncheck and save to remove a trigger. Multiple emails can share a trigger; every match queues on every change.
You have to save the email before you can check triggers. The UI shows a “Save the email first” note when you’re creating a new email.
How the send works
- An admin (or webhook, or API) changes a shipment’s status.
- The plugin writes the new status and a history row in a single save.
- For every enabled email whose triggers match the new status, the plugin queues a send job.
- The queue push happens in the same save, so an email can’t queue if the status change rolls back. No orphan emails, no lost sends.
- The queued job runs in the background, renders the template, and sends through Craft’s mailer.
Disabled emails
Flipping Enabled off stops all future sends. Jobs already in the queue still run. It’s all-or-nothing per email. Clone the email if you want one version live and another paused.
Language
The email renders in the order’s language by default, or in a specific site language if you set the email’s language field. The plugin switches the active language before rendering, the same way Commerce does for its own emails.
Common patterns
Customer-facing milestone:
- “Your order has shipped”, bound to
shipped.
Internal alerts:
- “Shipment on hold”, custom recipient
warehouse-lead@your-store.example, bound toon_hold. - “Shipment cancelled”, custom recipient
cs@your-store.example, bound tocancelled.
A “please ship this” email for 3PLs:
- Custom recipient for the 3PL dispatch inbox, bound to
in_progress.
Testing
There’s no “test email” button yet. To check an email:
- Create a test order and stage a shipment.
- Change the shipment to the status you’ve bound the email to.
- Check Craft’s queue (Utilities -> Queue Manager) for the send job.
- Check the recipient inbox (or
settings.toAddressin dev to catch all local mail).
What breaks if…
- You delete an email. All its transition bindings delete with it. Future matching changes no longer fire it. History and already-sent emails are unaffected.
- You point the HTML template at a path that doesn’t exist. The next send fails, logs the error, and the queue marks the job failed per its retry policy. Fix the path; jobs don’t retry on their own after the queue gave up, so requeue them.
- A Custom recipient doesn’t render to a valid address. That portion of the send is skipped; the rest proceeds. Failures are logged.