Test that your WooCommerce order emails actually arrive

WooCommerce order emails are the most reliably broken transactional email on the web, and the failure is silent by design: WordPress sends with wp_mail(), wp_mail() hands off to PHP’s mail(), mail() hands off to whatever the host has configured, and every layer reports success. The customer just never gets a receipt.

Nothing in the WordPress admin will tell you. The order is marked complete. The log, if there is one, says the mail was sent. It was — into a void, or into a spam folder, because the host’s IP has no SPF alignment with your domain.

Two ways to actually find out.

The quick way: send a real order email to a real inbox

Install the InboxDome Email Tester plugin and open Tools → Email Tester. It sends a genuine WooCommerce order-confirmation template to a temporary inbox and reports back:

That last one is the part a local “test email” button can never tell you, and it is usually where the answer is. A store whose mail arrives but fails DMARC is one policy change at Gmail away from silently vanishing.

The thorough way: assert on it in CI

If you deploy the store, test the order email like any other critical path. Create an inbox, place an order against it, and assert on what comes back.

<?php
// A real address that will receive.
$inbox = json_decode(wp_remote_retrieve_body(wp_remote_post(
    'https://inboxdome.com/v1/inboxes',
    [
        'headers' => [
            'Authorization' => 'Bearer ' . getenv('INBOXDOME_KEY'),
            'Content-Type'  => 'application/json',
        ],
        'body' => '{}',
    ]
)), true)['inbox'];

// … place an order with $inbox['emailAddress'] (WC_Order, WP-CLI, or a checkout run) …

Then assert — one call waits for the mail and checks every condition:

curl -X POST "https://inboxdome.com/v1/inboxes/$INBOX_ID/assert" \
  -H "Authorization: Bearer $INBOXDOME_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "timeoutSeconds": 60,
    "assertions": [
      { "type": "containsText", "text": "Thank you for your order" },
      { "type": "containsText", "text": "Order #" },
      { "type": "auth", "check": "spf",   "expect": "pass" },
      { "type": "auth", "check": "dkim",  "expect": "pass" },
      { "type": "auth", "check": "dmarc", "expect": "pass" }
    ]
  }'
{
  "passed": false,
  "arrived": true,
  "arrivalSeconds": 4.2,
  "results": [
    { "type": "containsText", "passed": true, "detail": "found" },
    { "type": "containsText", "passed": true, "detail": "found" },
    { "type": "auth", "passed": true, "detail": "spf=pass" },
    { "type": "auth", "passed": false, "detail": "dkim=none" },
    { "type": "auth", "passed": true, "detail": "dmarc=pass" }
  ]
}

That is the useful failure. The mail arrived, in four seconds, with the right content — and it is not DKIM-signed. Which means the moment the store’s volume gets a mailbox provider’s attention, receipts start going to spam. You would never have learned that from the order screen.

Give it 60 seconds rather than 30: WooCommerce sends order mail on a hook during checkout, and on a busy or cheap host that can queue behind other work.

What to test, in priority order

  1. The order confirmation arrives. Everything else is secondary.
  2. DKIM passes. This is the one that is most often quietly absent, and it is the one that gets a store’s mail filtered.
  3. The order number and total are in it. Catches a broken template that renders but loses its variables.
  4. Password reset arrives. A locked-out customer is a support ticket. See how to test a password reset email.
  5. The “new order” mail to the admin arrives. A store that has stopped notifying its owner of orders is a store losing orders.

Fix the usual cause first

If DKIM or SPF is failing, the fix is upstream of any test: stop sending through the host’s mail() and send through a provider that signs your mail (SendGrid, Postmark, Mailgun, SES) with an SMTP plugin. Then re-run the assertions and watch them turn green — which is the point of having them.

See also: WordPress and WooCommerce guide · PHP client · API reference


Get a free API key — 50 inboxes a day, no card. Orgrab a temporary inbox without signing up at all.