A WooCommerce store that takes payments and shows the "order received" page can still be silently broken on the email side. The customer never gets the confirmation, or it lands in Promotions, or the completed-order notification fires for nothing because the status never advanced.
This guide is for the operator who can configure SMTP but needs a map of the WooCommerce-specific knobs: which A transactional email is the automated message a WordPress site sends in response to a single user action – a password reset, an order confirmation, a form receipt – addressed to the user who triggered it. Read full reference → is driven by which trigger, and which failure modes are upstream (status-machine), sideways (relay behaviour), or downstream (mailbox-provider classification). The diagnostic axis runs across all three; the settings page at WooCommerce -> Settings -> Emails answers maybe a tenth of the questions an operator actually has.
The transactional email matrix and what fires each one
WooCommerce’s core transactional email types divide into admin-facing and customer-facing, and each is wired to a specific status transition or a direct action hook. The canonical eleven, as shipped in WooCommerce 10.8.1’s default install:
| Email ID | Recipient | Trigger | Default subject pattern |
|---|---|---|---|
new_order |
Admin | Order enters processing or on-hold (configurable) |
[{site_title}]: New order #{order_number} |
cancelled_order |
Admin | Order transitions from processing or on-hold to cancelled |
[{site_title}]: Order #{order_number} has been cancelled |
failed_order |
Admin | Order transitions from pending or on-hold to failed |
[{site_title}]: Order #{order_number} has failed |
customer_on_hold_order |
Customer | Order transitions into on-hold |
Your {site_title} order has been received |
customer_processing_order |
Customer | Order transitions into processing |
Your {site_title} order has been received |
customer_completed_order |
Customer | Order transitions into completed |
Your {site_title} order is complete |
customer_refunded_order |
Customer | Full refund processed | Your {site_title} order has been refunded |
customer_partially_refunded_order |
Customer | Partial refund processed | Your {site_title} order has been partially refunded |
customer_invoice |
Customer | Manual send from order admin | Invoice for order #{order_number} |
customer_note |
Customer | Admin adds a customer-visible order note | Note added to your {site_title} order |
customer_reset_password |
Customer | Lost-password request | Password reset for {site_title} |
customer_new_account |
Customer | New customer account created | Your account on {site_title} |
Two further types ship disabled by default: customer_failed_order (the customer-facing counterpart to the admin failed_order email) and customer_cancelled_order. Both stay dark until enabled in Settings -> Emails. WooCommerce 10.x has added fulfillment, abandoned-cart, POS, and review-request email types; all are feature-gated or off by default and not part of the default-install eleven.
Methodology: the eleven-email enumeration above was verified against plugins/woocommerce/includes/emails/class-wc-emails.php on WooCommerce 10.8.1, the current stable release as of 2026-06-19. The types listed are those registered and enabled in a fresh install.
The hook surface a developer or troubleshooter must know:
woocommerce_order_status_changedfires on every status transition with the order ID, old status, new status. Most customer-facing order emails listen for the more specificwoocommerce_order_status_{old}_to_{new}_notificationorwoocommerce_order_status_{new}_notificationvariants registered in each email class’s__construct().woocommerce_email_classesis the filter that registers the email types. Custom or replacement email types are added here.wp_mail_failedfires when PHPMailer throws. Plugins that log email or surface failures hook this.
The triggers are not interchangeable. When a customer reports a missing email, the first diagnostic question is which of the eleven did not fire, which is a question about status transitions.
Settings -> Emails: the fields that matter
The settings page exposes three layers: a global block applying to every WooCommerce email, the per-email-type toggles and templates, and a small set of template-rendering controls (colours, header image, footer text). The fields that operationally matter, in order of how often they cause problems:
From Address. Defaults to the WordPress admin email, which is usually a personal address from the operator’s day-to-day mailbox provider ([email protected], [email protected]), not an address at the store’s own domain. Every order email sent from that address fails SPF alignment because the From: header domain (gmail.com) does not match the relay’s authorised sending domain (example-store.com). The fix is to set From Address to an alias at the store’s own domain, typically [email protected], and ensure the domain has SPF, DKIM, and DMARC records authorising the chosen relay.
From Name. Less critical than From Address but still visible to the customer. The store name is the safe default; an actual person’s name reads as marketing and erodes the transactional signal.
Per-email-type enable toggles. Each email row has an Enable/Disable toggle. The lever most operators miss: a store that does not use the on-hold status (because it takes only fully-captured payments) can disable customer_on_hold_order and stop generating an email type that will never represent a useful event. The same applies to customer_partially_refunded_order for stores that never partially refund, and customer_cancelled_order for stores that auto-cancel only abandoned pending orders.
Recipients. The admin-facing emails (new_order, cancelled_order, failed_order) accept comma-separated recipient lists. The default is get_option('admin_email'), the same admin email used as the From: default. A store with separate operations and accounting workflows should set this to a shared inbox or a list address.
Header image, footer text, base colours. Template fields. The header image is loaded by the customer’s mail client over HTTP; a broken URL or a CDN that blocks hotlinking produces a missing-image placeholder in every order email. The footer text renders as plain text below the email body; legal footers (registered company number, VAT ID) go here.
The settings page does not expose the underlying templates. Those live at plugins/woocommerce/templates/emails/ and override into the active theme at wp-content/themes/{theme}/woocommerce/emails/. Customising the template is appropriate for branding; doing so to fix deliverability is almost always solving the wrong problem.
SMTP integration: routing WooCommerce through a real relay
WordPress core’s wp_mail hands off to PHP’s mail(), which on most shared hosts is a thin wrapper around sendmail: no authentication, no DKIM signature, no retry on transient failure, and a sending IP shared with every other site on the host. Transactional email sent this way works until it does not: a single complaint from a Gmail recipient drops the shared IP’s reputation, and order confirmations start landing in Promotions or spam across the customer base.
A real relay solves the same set of problems: authenticated submission (SMTP over TLS or HTTPS API), DKIM signing per message, a sender-domain reputation independent of the host, retry on soft bounce, and a per-message delivery log.
There are two practical ways to route WooCommerce through a relay. The first is the mailer-plugin path: install WP Mail SMTP, FluentSMTP, or Post SMTP, configure it against a transactional provider, and let the plugin override wp_mail for the entire site. WooCommerce’s email engine sits on top of wp_mail, so anything routed through the mailer plugin covers every order email, every password reset, every contact-form submission, and every notification a third-party plugin sends. This is the path nanoPost recommends in almost every case.
The second is provider-specific WooCommerce integration: a few providers (Mailgun, Brevo) ship WooCommerce-aware plugins that capture WooCommerce mail specifically. These cover only WooCommerce’s own emails and leave password resets, third-party plugin email, and contact-form submissions on the broken wp_mail default. A store with a contact form, a membership plugin, or a marketing-list plugin will end up with two email pipelines: one for WooCommerce and one for everything else. Two pipelines mean two sets of authentication records, two reputation pools, and two places to debug a missing message.
The mailer-plugin layer is also where logging happens. WP Mail SMTP Pro, FluentSMTP (free), and Post SMTP all maintain a per-message log of every wp_mail call: timestamp, recipient, subject, SMTP response code, rendered message body. This is the diagnostic surface for "the customer says they did not get the email". Without a log, the answer is always "I do not know whether WooCommerce called wp_mail at all"; with a log, the answer is one of three: wp_mail was not called (a status-machine issue), wp_mail was called and the relay returned an error (authentication or rate-limit), or wp_mail was called and the relay returned 250 OK (a deliverability issue at the receiving mailbox provider).
The general SMTP plugin landscape covers the cross-plugin comparison. The WooCommerce-specific question is narrower: which mailer plugin and which transactional provider, paired, fit a store at a given volume.
Provider pairings for WooCommerce
Three pairings cover the operating range of most WordPress-driven stores. Each is named with the volume tier where it stops being the right pick.
| Volume tier | Plugin + provider | Why this pairing |
|---|---|---|
| Up to ~1,000 msg/month | FluentSMTP + SMTP2GO (free) | Permanent free tier, logging in the free plugin, no upsell |
| 1,000 – ~50,000 msg/month | WP Mail SMTP + Postmark | Separate transactional IP pool, full per-message log |
| 50,000+ msg/month | WP Mail SMTP + Amazon SES | $0.10/1,000 dominates economically; needs SES operational discipline |
Postmark and WP Mail SMTP, for stores where transactional reliability matters more than per-message cost. Postmark separates transactional and broadcast streams at the account level: order email goes through a dedicated transactional IP pool that never carries marketing mail, which keeps order email out of the reputational fallout when a campaign generates complaints. WP Mail SMTP logs every wp_mail call with body, recipient, subject, and SMTP response, so a missed order email is one query, not a guess. The pairing fits stores up to roughly 50,000 messages per month. Above that, Postmark’s per-message pricing starts to outpace SES or SMTP2GO substantially.
SMTP2GO and FluentSMTP, for the cost-conscious shop. SMTP2GO ships a permanent 1,000-per-month free tier with a 200-per-day cap, enough for a small store sending order confirmations only. FluentSMTP ships email logging and resend-failed in its free version, so the diagnostic surface and one-click replay come without an upgrade prompt. The free pairing fits stores under 1,000 messages per month; SMTP2GO Starter ($10/month for 10,000) extends it to about 10,000. Above that, SES becomes meaningfully cheaper and Postmark meaningfully more reliable for transactional-only volume.
Amazon SES and WP Mail SMTP, for higher-volume stores past roughly 10,000 messages per month. Amazon SES prices at $0.10 per thousand messages with no monthly minimum, which dominates economically at any volume past Postmark’s lower paid tiers. The cost is operational: SES requires IAM-credential setup, sandbox approval before sending to unverified addresses, and ongoing bounce-and-complaint monitoring (SES places an account under review at a 0.1% complaint rate and pauses sending at 0.5%). WP Mail SMTP’s SES integration handles the IAM and SMTP-credential dual setup more cleanly than the alternatives. The pairing fits stores that have outgrown Postmark’s pricing and have the operational maturity for SES’s compliance demands.
A small store evaluating only free tiers has a fourth option: Resend offers 3,000 messages per month free (100 per day), with a cleaner API than SMTP2GO. Current WordPress plugin support is thinner, which is why SMTP2GO remains the free-tier pick when paired with FluentSMTP.
Mailgun ships a permanent free plan (100/day, API and SMTP, one domain), but the daily cap is the constraint at WooCommerce scale: five orders per day generates around fifteen messages between customer and admin, and a single password-reset campaign blows through 100 by mid-morning. Mailgun is a fine relay for the non-WooCommerce side of a site; for the order pipeline, SMTP2GO or Postmark price more predictably as volume grows.
The five WooCommerce email failures and how to fix them
"The customer didn’t get the confirmation but I got the admin notification"
This is a trigger-map question first and a deliverability question second.
When an order transitions from pending to processing (the default flow after a successful payment), two emails are scheduled: new_order to the admin and customer_processing_order to the customer. Both fire on the same hook (woocommerce_order_status_pending_to_processing_notification) and both hit wp_mail within milliseconds of each other. If the admin email arrived and the customer email did not, the diagnostic order is:
- Check the SMTP log for both messages. If
wp_mailwas called forcustomer_processing_orderand the relay returned250 OK, the message left WordPress and the issue is at the customer’s mailbox provider (spam, Promotions, aggressive filter). Send the customer a manualcustomer_invoicefrom the order page to confirm a different email path works. - If
wp_mailwas not called, the email type is either disabled in Settings -> Emails (check the Enable toggle) or the order has an empty billing email (guest checkout with a broken form). - If
wp_mailwas called and the relay returned an error, the From: address probably does not match the relay’s authorised domain. This usually accompanies every transactional email failing, not just the customer-facing ones.
"The completed-order email never sends"
This is almost always a status-machine issue, not an email issue.
customer_completed_order listens for woocommerce_order_status_completed_notification, which fires only on transition into the completed status. If a store’s workflow goes pending -> processing and stops there (because a fulfillment plugin marks orders shipped without advancing the status, or because the operator marks orders complete in a separate ERP), the transition into completed never happens and the email never fires.
The fixes:
- If the fulfillment plugin has its own shipped-notification email, disable
customer_completed_orderin Settings -> Emails and let the plugin handle the customer message. Two emails saying "your order shipped" is worse than one. - If the operator wants WooCommerce’s completed-order email to fire on shipment, the fulfillment plugin must call
$order->update_status('completed')when it marks the shipment. Most do not by default; a small custom hook on the plugin’s "shipped" action closes the gap. - If the operator marks orders complete in a separate system and never touches WooCommerce, the WooCommerce status stays at
processingindefinitely. The right move is either a periodic sync (a cron that transitions WooCommerce orders tocompletedbased on the external state) or to disable the email and stop wondering why it does not arrive.
The log surface for this failure is distinctive: no log entry at all for customer_completed_order, because wp_mail was never called. Contrast with the previous failure mode, where wp_mail was called and the question is what happened after.
"Order emails are landing in Gmail’s Promotions tab"
WooCommerce order emails contain the structural pattern Gmail’s classifier most often files as Promotions: a dollar amount near the top, a product image, a line-item table, a "Thank you for your order" greeting, and a footer link block. The classifier is opaque and operator-uncontrollable; the steps that help are partial and the result is "the email lands in Primary more often", not "the email always lands in Primary".
The steps that help, partially:
- Send from an unambiguously transactional sender alias.
[email protected]reads more transactional than[email protected]. Avoidmarketing@,news@, or any address used for the broadcast newsletter. - Do not add
List-Unsubscribeheaders to transactional email. Some mailer-plugin configurations add the header globally; transactional email should not have an unsubscribe link, and the header’s presence is one of the signals Gmail uses to categorise mail as Promotional or commercial. - Keep the order-email template plain-text-leaning. A logo at the top and a simple line-item table is fine; a full marketing-style template with promotional banners, related-product carousels, and social media links is not.
- Ensure DMARC alignment is passing. A transactional message that fails DMARC is more likely to be filtered (Spam, Promotions, or rejected outright) regardless of content. See DMARC aggregate reports for how to confirm alignment is working.
- Sign up the sender at Gmail Postmaster Tools. This does not change classification directly, but it surfaces the spam rate, the IP reputation, and the domain reputation Google sees, which is the first place to look if a store goes from Primary to Promotions overnight.
Gmail’s classifier wins the final call. A store that has done all of the above and still sees Promotions placement should accept that the email is arriving, the customer can find it, and the relationship between "transactional" and "Primary tab" is not enforced.
"Cart-abandonment emails are missing or going to spam"
Cart abandonment is not part of core WooCommerce. Every cart-abandonment email comes from a plugin (CartFlows, AutomateWoo, FunnelKit Automations, Klaviyo, ActiveCampaign) that has its own email engine, its own template system, and sometimes its own sending infrastructure.
The WooCommerce-specific landmine: cart-abandonment messages are marketing rather than transactional under any working definition. They are unsolicited, sent after the customer has not completed an action, and frequently include promotional language with a discount code. Sending them through the same domain and IP that carries order confirmations puts the transactional reputation at risk if the abandonment campaign generates complaints.
The right architecture is sender segmentation: a separate From: address, ideally a separate subdomain, for marketing-shaped messages. [email protected] carries transactional traffic; [email protected] carries the abandonment cart, the newsletter, and any other marketing-pattern email. The two subdomains can share a relay, but each maintains an independent reputation; a complaint storm on the marketing subdomain does not poison the transactional one. Of the WooCommerce cart-abandonment options, Klaviyo runs its own ESP infrastructure and exposes per-campaign From: addresses by design; AutomateWoo, FunnelKit Automations, and CartFlows route through wp_mail and inherit the site’s global From: address unless the operator splits marketing-shaped mail onto a second relay (a second mailer plugin instance configured for the marketing subdomain, or a non-wp_mail send path).
"We are crossing the bulk-sender threshold"
Stores doing roughly 5,000 messages per day from a single domain cross Gmail and Yahoo’s bulk-sender thresholds (5,000 per day to Gmail recipients alone). The thresholds bring three new requirements that smaller stores can ignore:
- A published DMARC record, with an enforcement policy of at least
p=none. The published policy is the floor;p=quarantineorp=rejectis recommended for transactional senders past this threshold, butp=noneclears the rule. - A working
List-Unsubscribeheader with the one-click variant (List-Unsubscribe-Post: List-Unsubscribe=One-Click) for any marketing-shaped mail. Transactional order email is exempt from the unsubscribe requirement. - A complaint rate below 0.3% as measured at Gmail Postmaster Tools, with sustained periods below 0.1% required to stay in good standing.
Most WooCommerce shops do not think of themselves as bulk senders, but combined order email plus customer-account email plus abandoned-cart campaigns plus a weekly newsletter gets there fast. A store at 200 orders per day sends roughly 600 transactional messages (new_order admin, customer_processing_order customer, customer_completed_order customer, plus refunds, invoices, password resets); combined with even a small marketing list, that lands in bulk-sender territory.
The fixes are not WooCommerce-specific; they are deliverability hygiene. Publish a DMARC record, monitor aggregate reports, segment marketing from transactional.
Verifying WooCommerce email works end to end
Configuration is necessary but not sufficient; the only thing that confirms WooCommerce email works is observing the diagnostic sequence below fire against a real order.
The diagnostic test order, performed in a staging environment with a real mailbox the tester controls:
- Place a complete checkout that takes the order from cart -> pending -> processing. At the
pending -> processingtransition, expect:new_orderto the admin,customer_processing_orderto the customer. Check the SMTP log for both. Check both inboxes for arrival. - Manually transition the order from processing -> completed via the order admin. Expect:
customer_completed_orderto the customer. Check the log. Check the inbox. - Issue a partial refund. Expect:
customer_partially_refunded_order. Repeat the log-and-inbox check. - Issue a full refund on a different test order. Expect:
customer_refunded_order. - Send a
customer_invoicemanually from the order admin. Expect arrival. - Add a customer-visible note from the order admin. Expect:
customer_note. - Trigger a password reset from the customer login flow. Expect:
customer_reset_password. - Place a second checkout with a card that the gateway will decline. Expect:
failed_orderto the admin, and (if enabled)customer_failed_orderto the customer.
For each, the verification has three parts: the SMTP log shows wp_mail was called for the expected email type; the relay returned 250 OK or its equivalent; and the message arrived in the test inbox. A missing message at any of those three layers is a different problem with a different fix.
Inbox placement is a separate question. Landing anywhere is the bar this verification clears; Primary-versus-Promotions confirmation requires a seed-list test or an inbox placement test, and a store that has passed all eight diagnostic steps without that confirmation is operating on hope at the placement layer.
Verdict
The shortest path to working WooCommerce email: install WP Mail SMTP or FluentSMTP, point it at Postmark or SMTP2GO or SES depending on volume, change the From Address from the WordPress admin email to an orders@ alias at the store’s own domain, ensure SPF, DKIM, and DMARC records authorise the chosen relay. Enable email logging, run the test order from the verification section, watch the messages arrive.
The harder problem is the long tail: status-machine gaps that mean the completed-order email never fires, content-classifier issues that file order email under Promotions, sender-reputation concerns when transactional and marketing share a domain, the bulk-sender thresholds a growing store crosses without realising. This guide named the common cases and the diagnostic path for each. The general WordPress email troubleshooting flow is covered in the troubleshooting guide; the WooCommerce-specific layer is above.
A store that has done the work here is sending order email through a real relay, on a properly authenticated domain, with a logging surface that answers the diagnostic questions when they come, and with a defensible provider choice for its volume. That store will still have email problems occasionally; everyone does. It will not have email problems it cannot diagnose.
