Shop emails
WooCommerce sends emails on order events by itself: order received and order completed to the customer, new order to the administrator. Every email is a separate class with its own ID, settings and template. Texts are changed in the admin panel, and content is extended with hooks.
Emails in the admin panel
The list of emails is in WooCommerce → Settings → Emails. Each has a Manage button: there you enable the email and change the subject, the heading and the additional content. The bottom of the page holds the common look: the logo, colors, footer text.
| ID | To whom | When |
|---|---|---|
new_order | The administrator | A new order is created |
customer_on_hold_order | The customer | The order is on hold |
customer_processing_order | The customer | The order is paid |
customer_completed_order | The customer | The order is completed |
customer_refunded_order | The customer | A refund |
customer_note | The customer | A note to the customer was added |
customer_new_account | The customer | An account was created |
Where to write the code
Create the includes/emails.php file and include it in acme-shop.php.
The email subject
// wp-content/plugins/acme-shop/includes/emails.php
add_filter('woocommerce_email_subject_customer_processing_order', function (string $subject, $order): string {
if (!$order instanceof WC_Order) {
return $subject;
}
return sprintf('Order #%s is in progress', $order->get_order_number());
}, 10, 2);The filter name is woocommerce_email_subject_ plus the email ID. Place an order: the customer's email arrives with the subject Order #1024 is in progress. The heading inside the email is changed the same way, with the woocommerce_email_heading_ filter and the same ID.
Adding content
// wp-content/plugins/acme-shop/includes/emails.php
add_action('woocommerce_email_before_order_table', function (WC_Order $order, bool $sentToAdmin, bool $plainText, $email) {
if ($email->id !== 'customer_completed_order') {
return;
}
$tracking = $order->get_meta('_acme_tracking');
if ($tracking !== '') {
echo '<p>Parcel tracking number: <strong>' . esc_html($tracking) . '</strong></p>';
}
}, 10, 4);The hook fires in every email with an order table, so the function checks the email ID. In the Completed order email the tracking number from the previous lesson appears above the product table.
Email hooks
| Hook | Where in the email |
|---|---|
woocommerce_email_header | The header |
woocommerce_email_before_order_table | Before the order table |
woocommerce_email_after_order_table | After the order table |
woocommerce_email_order_meta | Additional order details |
woocommerce_email_customer_details | The addresses block |
woocommerce_email_footer | The footer |
The four hooks around the order receive the same arguments: the order, a sent-to-admin flag, a plain-text flag and the email object.
Testing without an order
To avoid placing an order for every email, open an order in the admin panel, choose Send order details to customer or a resend of the needed notification in the Order actions box and press the arrow button. On a local server emails are convenient to catch with Mailpit.
Overriding a template
The email markup is changed with a template copy, as in the lesson on templates:
Source file in woocommerce/templates/ | Copy in the theme |
|---|---|
emails/customer-completed-order.php | acme-child/woocommerce/emails/customer-completed-order.php |
emails/email-header.php | acme-child/woocommerce/emails/email-header.php |
emails/email-styles.php | acme-child/woocommerce/emails/email-styles.php |
The email-styles.php file holds the CSS of all emails. Email copies go out of date just like other templates — watch the Templates section on the WooCommerce → Status page.
Disabling an email in code
// wp-content/plugins/acme-shop/includes/emails.php
add_filter('woocommerce_email_enabled_customer_completed_order', function (bool $enabled, $order): bool {
return $order instanceof WC_Order && $order->get_shipping_method() === '' ? false : $enabled;
}, 10, 2);The Completed order email is not sent for orders without shipping — for example, a pickup placed without a shipping method.
Common mistake
Emails do not arrive, and the cause is sought in the code. WooCommerce passes an email to wp_mail(), which hands it to the server's mail service — on hosting it is often missing or its mail lands in spam. Set up sending through SMTP with a separate plugin and check delivery before changing templates.