Why Your WooCommerce Abandoned Cart Emails Aren't Sending (and How to Fix It)
You switched on abandoned cart emails, carts are clearly being abandoned, and the recovery emails never go out, go out hours late, or arrive after the customer has already paid. The settings say everything is enabled, so it's hard to tell what's broken.
An abandoned cart email is a chain of four steps, and each one fails in its own way. Find the step that's breaking and the fix is usually quick.
The four steps every abandoned cart email depends on
- Capture. Something has to record the cart and an email address to send it to.
- Timing. Something has to notice the cart went quiet and decide it's abandoned.
- Delivery. The email has to be sent and land in the inbox, not spam.
- Stopping. When the shopper buys, the series has to end.
Before you change any settings, run one test so you know which step to look at:
- Open your store in a private browser window, so you're a guest with no login.
- Add a product, go to checkout, type an email address you can check, and leave without paying.
- Wait for the delay your flow uses, then look in that inbox, including the spam folder.
No cart recorded for that address means capture. A cart but no email sent means timing. Sent but never arrived means delivery. Then repeat the test and pay: reminders after that mean the stop signal.
Step 1: The cart was never captured
This is the most common cause, and it is rarely a bug. You can only email a shopper whose address you have. A guest who closes the tab before reaching checkout is anonymous, and no plugin can email them. A cart becomes recoverable once the shopper types an email at checkout or is logged in.
Things to check:
- Is the email captured as it's typed? If your setup only learns the address when the order is submitted, it misses exactly the shoppers you're trying to reach.
- Does it work on your checkout? WooCommerce has two checkouts: the newer Checkout block and the classic shortcode checkout. A plugin that only listens to the classic
#billing_emailfield captures nothing on the block checkout. - Is a consent setting filtering people out? If you only capture shoppers who tick a marketing checkbox, most carts won't qualify. That can be the right call for privacy, but expect far fewer recoverable carts.
- Is a cache or firewall blocking the capture request? A page cache that serves checkout with a stale nonce, or a firewall rule on
admin-ajax.php, can quietly reject it.
In the free PostBeagle for WooCommerce plugin, cart capture is off by default and lives on the Cart Recovery tab of the plugin's settings. Three settings there decide what gets captured:
- Capture carts has to be ticked, and PostBeagle has to be switched on under the Setup tab, or nothing is captured at all.
- Capture mode has two options. "Capture during checkout" sends the shopper's email and cart contents to PostBeagle as they fill in checkout, on both the block and classic checkouts. "Only after a completed order" captures nothing before purchase, which means there are no abandoned carts to recover. If you picked that mode for privacy reasons, it explains why you aren't getting any reminders.
- Privacy mode only captures shoppers who have ticked the opt-in checkbox. With it on, logged-in customers who haven't ticked the box aren't captured either.
Step 2: The cart was captured, but the timer never fired
If the cart exists but the email goes out hours late or not at all, look at scheduling. Most WordPress cart recovery plugins schedule the "wait an hour, then send" step with WP-Cron, which only runs when someone loads a page. On a quiet store at 2am, a reminder due at 2:15 waits until the next visitor arrives, which might be breakfast time. WooCommerce's Action Scheduler, which many plugins use for background jobs, is kicked off by WP-Cron and by admin page loads, so on a store where nobody is browsing or logged in, it stalls too.
How to tell:
- Go to WooCommerce → Status → Scheduled Actions and open the Past-due filter. A pile of past-due actions means your background jobs aren't running on time.
- Check
wp-config.phpfordefine( 'DISABLE_WP_CRON', true );. It stops WP-Cron running on page loads, and without a system cron to replace it, scheduled jobs don't run at all. - Check whether a security plugin, HTTP basic auth on a staging copy, or a firewall rule is blocking requests to
wp-cron.php.
The fix is the one the WordPress developer handbook recommends: turn off the page-load trigger and run WP-Cron from a real scheduler on your server. Add this to wp-config.php:
define( 'DISABLE_WP_CRON', true );
Then add a system cron job (via crontab -e or your host's cron panel) that calls wp-cron.php every five minutes:
*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
Replace example.com with your domain. If your host has WP-CLI, wp cron event run --due-now from the site's directory does the same job without an HTTP request. Many managed hosts already run a server cron, so ask first.
PostBeagle's "Capture during checkout" mode doesn't put the abandonment timer in WP-Cron. PostBeagle decides when a cart has gone quiet, and the wait steps in your abandoned cart automation set when each email goes out. That's also why the plugin's own delay field is grayed out in that mode: edit the timing in the automation instead. The purchase signal that stops the series is still queued in Action Scheduler, though, so a healthy cron matters for step 4 either way.
Step 3: The email was sent, but it went to spam
If your tool shows the email as sent and the shopper never saw it, you have a delivery problem, and cart reminders are especially exposed to it.
- Where is it sent from? Many WordPress-side recovery plugins send through
wp_mail(), which means your web host's mail server unless you've added an SMTP service. Shared hosting IPs often have a poor reputation, and mail from them regularly lands in spam or is rejected outright. - Is your domain authenticated? Gmail and Yahoo expect bulk senders to have SPF, DKIM and DMARC in place, with the visible From domain aligned to the domain that passes SPF or DKIM. Google's sender guidelines list the requirements. If you already have DMARC at
p=noneand want to tighten it, this guide to moving DMARC to p=reject walks through doing it without blocking your own mail. - Does it look like a promotion blast? A plain subject line, the real product and one clear button reads like a nudge. Three discount banners and "LAST CHANCE" in the subject get filtered like a promotion.
You can check your domain's SPF, DKIM and DMARC records for free with the PostBeagle deliverability checker, no account needed. For everything else that pushes mail toward the spam folder, see why emails go to spam and how to fix it.
Don't judge delivery by opens. Apple Mail Privacy Protection loads images automatically for many readers, which inflates open rates. Clicks on the recovery link and recovered orders tell you the email reached a person.
Step 4: Reminders keep going out after the customer paid
A "you left something in your cart" email to someone who just paid is worse than no email at all. It usually comes from one of these:
- The stop signal depends on an order status your flow doesn't watch. Bank transfer (BACS) and cheque orders sit in On hold until the payment clears, sometimes for days. If the recovery series only stops at Processing or Completed, those customers get the full series.
- The shopper paid with a different email address. The cart is tied to the address they typed first, so a tool that only matches the billing email never sees the purchase.
- The job that sends the signal is stuck. If the "order placed" event is queued in Action Scheduler and cron isn't running, it arrives after the next reminder. That's the step 2 problem again.
The PostBeagle plugin covers the first two. When an order goes to processing or completed, or goes on hold, the plugin tells PostBeagle the cart converted. It sends every email address that identified that shopper during the visit, not only the billing address. The on-hold signal is sent straight away, but the processing/completed signal is queued in Action Scheduler, so fix cron first if reminders still arrive after a paid order.
A quick checklist
- Cart capture is switched on, and the capture mode actually captures carts before purchase.
- A guest test on your live checkout (block or classic) shows up as a cart with an email address.
- WooCommerce → Status → Scheduled Actions has no growing pile of past-due actions.
- If
DISABLE_WP_CRONis set, a system cron callswp-cron.phpevery few minutes. - Reminders go out from an email service with SPF, DKIM and DMARC passing, not your web host.
- A completed test purchase stops the series, including for a bank transfer order on hold.
If you'd rather not maintain all of this
PostBeagle's abandoned cart recovery for WooCommerce captures the email at checkout, decides abandonment on PostBeagle's side instead of in WP-Cron, sends reminders from PostBeagle's own sending infrastructure, and stops the series when the order is placed. The plugin is free and cart recovery works on the free plan.
