Lifecycle & marketing
Build an abandoned cart flow with Klaviyo and n8n
Klaviyo's built-in abandoned cart flow is good — when its onsite tracking actually sees your checkout. When it doesn't (a headless storefront, a custom cart, a checkout the snippet never loads on), the recovery emails just don't fire. Here's how to read the abandonment straight from Shopify with n8n, guard it so you never email someone who already bought, and hand Klaviyo a reliable event to send on.
- Klaviyo
- n8n
- Shopify
The manual way today
There usually isn't a manual version — that's the trap. Most stores 'set up abandoned cart' once by turning on Klaviyo's native flow, see it go live, and assume it's working. But that flow triggers on a Started Checkout event that depends on Klaviyo's onsite JavaScript firing on the exact checkout page a customer reaches. On Shopify's standard checkout with the integration installed, it mostly does. On a headless front end, a custom or third-party checkout, or any page where the snippet doesn't load, it quietly doesn't — and nobody's watching a flow that's supposed to run itself. The 'manual process' is someone, weeks later, noticing the recovery revenue line is flat and digging into why.
An abandoned cart flow is usually one of the highest-return emails a store sends, because it reaches people who already chose the product and got within a click of paying. When it silently doesn't fire, you don't see an error — you see nothing, which reads exactly like 'we don't have many abandoners.' Every checkout that stalls is a warm buyer who gets no nudge, and there's no bounced email or failed task to tip you off. The cost isn't the build; it's the months of recoverable orders that walk because the one flow meant to catch them was triggering on a browser snippet that never ran.
What running looks like
Abandonment is read server-side from Shopify's checkout webhook — not from a snippet that may never load — so the trigger fires whether your checkout is standard, headless, or custom. n8n holds the cart for your abandonment window, re-checks that it didn't convert, confirms the items are still in stock, and only then pushes a clean 'Abandoned Checkout' event into Klaviyo. Klaviyo owns the cadence and the sends, spacing the follow-ups and exiting anyone who buys. One abandonment equals one reminder sequence; carts that recover or sell out get dropped automatically.
01The build
How to build it
Decide the trigger, the timing, and whether you even need n8n
Needs a humanSettle three things before wiring anything, and be honest about the third. First, the trigger: an abandoned cart means a Started Checkout (the customer reached checkout and gave an email or phone) with no order placed inside your window — not a bare add-to-cart, which has no identity to email. Second, the cadence: a common shape is the first email ~1 hour after abandonment, a second at ~24 hours, an optional third at ~72 hours. Third, and most important: if you're on Shopify's standard checkout and Klaviyo's onsite tracking is reliably firing Started Checkout, the native Klaviyo abandoned-cart flow already does this — build that and stop. Reach for n8n only when the native event doesn't fire (headless or custom checkout, the snippet not loading, a cart that lives outside Shopify) or when you need to guard the trigger with data the native event can't carry.
Capture the abandonment signal at the source
Runs itselfGet the signal from somewhere that doesn't depend on a browser. The reliable source on Shopify is the checkouts/create and checkouts/update webhooks: the abandoned-checkout object fires when a customer reaches checkout and provides contact details, and it carries everything you need — email and phone (with consent flags), the checkout token, line items, total price, currency, and the abandoned_checkout_url (the recovery link that drops them back into their cart). Point those webhooks at an n8n webhook node. If your checkout is fully custom or headless and doesn't emit Shopify's checkout object, POST your own 'checkout started' payload from the storefront to the same n8n webhook the moment the customer hits the checkout step, with those same fields.
Hold the open checkout and wait out the window
Runs itselfDon't fire the instant a checkout starts — most 'abandoners' are mid-purchase. Store each open checkout keyed by its checkout token, with its timestamp and payload, in something durable: an Airtable/Sheet row, a small database, or a Redis key. Then wait the abandonment window from step one before treating it as abandoned. A scheduled n8n sweep over the open-checkout store (every few minutes, picking up rows older than the window) is more robust than an in-memory Wait node, because it survives restarts and won't lose carts if n8n bounces mid-wait.
Confirm it actually didn't convert before firing
Runs itselfThis is the guardrail that prevents the single worst email in ecommerce — 'you left something behind!' sent to someone who already paid. Before enqueuing, check Shopify for an order matching the checkout token (or the email within the window); Shopify's orders/create webhook can also flip a 'converted' flag on your stored row as soon as the purchase lands. Drop any checkout that completed or was already recovered, and suppress if the same customer placed any order in the last little while. The cart has to still be genuinely open to move forward.
Enrich, check stock, and apply exclusions
Runs itselfNow decide whether this abandoner should be reminded at all. Re-check inventory on the cart's items — reminding someone about a product that's now sold out is worse than staying quiet, so skip those (or swap in an in-stock alternate if that's your style). Honor consent: only queue email or SMS for people who actually opted in. Drop wholesale and B2B checkouts, and skip anyone you don't want double-messaged if they're mid-sequence in another high-priority flow. Attach what the email will render: line-item titles, images, prices, quantities, the cart total, and the recovery URL.
Stamp an idempotency key so a cart enters once
Runs itselfThe moment a checkout clears the guards and gets enqueued, write a key to your store — the checkout token plus a 'reminded' flag and timestamp. A duplicate checkouts/update, the sweep running twice, or a manual re-run all see the key and skip. Dedupe on this key, not on the Klaviyo send, so a flaky retry can never push the same customer into the sequence twice.
Push a custom 'Abandoned Checkout' event into Klaviyo
Runs itselfPOST to Klaviyo's event API a custom metric — call it something like 'Abandoned Checkout (n8n)' — on the customer's profile, with the properties the messages need: total value, currency, the line items (title, image, price, qty), and the recovery URL. This server-side custom event is the whole point: it lands reliably regardless of whether onsite JavaScript ever saw the checkout, so the flow has a dependable thing to trigger on instead of a snippet that may never have fired.
Let a Klaviyo flow own the cadence and the sends
Runs itselfBuild a Klaviyo flow triggered on the 'Abandoned Checkout (n8n)' metric. Because n8n already held the abandonment window in step three, the cart is genuinely abandoned by the time the event lands — so the first message can send near-immediately, the second at ~24 hours, and an optional third at ~72 hours (this is where a discount belongs, if you use one). Add a flow filter that exits anyone who has placed an order since the trigger (Klaviyo's conversion suppression), and let Klaviyo handle quiet hours, throttling, and unsubscribes. Owning the sends here means consent and timing are respected for free, instead of you firing raw API blasts that ignore them.
Catch failures and watch the recovered-order rate
Runs itselfOn inventory and revenue work, a silently failed call is the expensive kind. Wrap the Klaviyo event POST in a bounded retry with backoff, and if it still fails, push the cart to a Slack or exception lane rather than dropping a recoverable order on the floor. Then watch the number that matters: the recovered-order rate of the flow. If it sags after a change, you'll usually find the window got too long, the second email turned naggy, or the stock check is suppressing more than it should — all tunable, none visible unless you're looking.
02The stack
What it’s built on
- Shopify
- The reliable, server-side source of the abandonment signal: its checkouts webhooks emit the abandoned-checkout object (line items, totals, and the recovery URL), and its orders webhook is how you confirm a cart did or didn't convert before reminding anyone.
- n8n
- The event source and the brain. It catches the checkout webhook (or your storefront's POST), holds the cart for the abandonment window, re-checks for conversion, runs the inventory and consent guards, dedupes, and only then fires the Klaviyo event.n8n is open-source and self-hostable for free; the cloud tier just saves you running it yourself. This is a build where it earns its place over Zapier — the open-checkout store, the conversion re-check, the stock guard, and the dedupe key need real branching and a persistent store, not single-step zaps.
- Klaviyo
- Owns the cadence and the messages. The custom 'Abandoned Checkout' event triggers a flow that spaces the follow-ups, exits anyone who buys, and respects consent, quiet hours, and unsubscribes automatically.Klaviyo's free tier is plenty to build and test this on, and you're almost certainly already running it. Worth saying plainly: its native abandoned-cart flow is genuinely good — if your onsite tracking fires Started Checkout reliably, use it and skip this build. This recipe is for when it doesn't.
03Questions
Before you build
Doesn't Klaviyo already have an abandoned cart flow built in?
It does, and it's good — so use it if it's working. The native flow triggers on a Started Checkout event that Klaviyo's onsite JavaScript records, which is reliable on Shopify's standard checkout with the integration installed. It falls down when that snippet doesn't fire: a headless storefront, a custom or third-party checkout, or any page the script never loads on. This build is for exactly that case — it reads abandonment server-side from Shopify and feeds Klaviyo a custom event instead, so the flow triggers whether or not onsite tracking ever saw the checkout.
How long should I wait before the first recovery email?
A common shape is the first email about an hour after abandonment, a second around 24 hours, and an optional third near 72 hours. The window matters more than people think: too short and you're nagging someone who just stepped away for coffee and would've finished anyway; too long and the intent has gone cold. Hold the abandonment window in n8n, then let Klaviyo space the follow-ups from there.
What stops someone who already bought from getting a 'you forgot something' email?
Two independent checks. Before n8n fires the event, it looks up Shopify for an order on the same checkout token or email inside the window and drops anything that converted. Then Klaviyo's flow has conversion suppression on top — it exits anyone who places an order after the trigger. A cart has to survive both to ever get a reminder, which is what keeps the worst email in ecommerce from going out.
Should the trigger be 'added to cart' or 'started checkout'?
Started checkout. You only have someone to email once they've reached checkout and given an email or phone — an add-to-cart with no identity attached has no recipient. Recovering people who browsed or added to cart but never identified themselves is a different automation (browse abandonment), which relies on onsite identification rather than a checkout signal.
Why route it through n8n instead of just using Shopify's Klaviyo integration?
If the integration's onsite tracking sees your checkout, don't — use the native flow. n8n is the answer when that signal is unreliable or missing. It reads the abandonment straight from Shopify's checkout webhook (or a POST from your own storefront) and pushes a clean custom event into Klaviyo, so the flow fires on a dependable server-side signal instead of a browser snippet that might never load. It also gives you a place to put the stock check, conversion re-check, and dedupe that the native event can't carry.
Can I build this myself, or should you build it?
Every trigger, webhook, field, and guardrail is laid out above, so if you're comfortable with the Shopify and Klaviyo APIs and have an afternoon, wire it up yourself. If you'd rather not own the open-checkout store, the conversion re-check, and the dedupe edge cases, we'll build it on the Klaviyo, n8n, and Shopify accounts you already pay for, document every piece, and hand you the keys — you own the workflow outright, with no monthly retainer. It starts with a free 20-minute teardown, not a contract, and you keep the plan either way.
Related recipes
Lifecycle & marketing
Build a browse abandonment flow for Shopify
Plenty of shoppers open a product page, look, and leave without ever adding to cart. A browse abandonment flow sends those people a quiet reminder of what they looked at. It's the lowest-intent recovery flow you'll build — and it has one hard limit worth saying out loud: it can only reach shoppers Klaviyo already knows. Here's how to wire it on your Shopify store, keep it from stepping on your higher-intent flows, and tell whether it's actually earning its place.
- Klaviyo
- Shopify
Lifecycle & marketing
Build a win-back flow for customers lapsed 90 days
A customer who used to order every month quietly stops, and nothing in your store flags it — you only chase brand-new strangers while people who already loved the product drift to a competitor. Here's how to build a Klaviyo win-back flow that catches them on real purchase cadence (not a blanket 90 days), reconnects before it discounts, and self-exits the moment someone buys. It's a native Klaviyo build — no n8n or glue layer required.
- Klaviyo
- Shopify
Lifecycle & marketing
Send replenishment reminders for consumable products
Your product is built to run out — that's the business. But if nothing reminds the customer the week they hit empty, the reorder either doesn't happen or happens on Amazon. Here's how to build a Klaviyo replenishment flow timed to each consumable's real run-out date, guarded so it never nudges someone who already restocked or is on a subscription. It's a native Klaviyo + Shopify build — no glue layer required for the common case.
- Klaviyo
- Shopify
Lifecycle & marketing
Exclude recent purchasers from Meta retargeting
Your retargeting ad sets keep serving 'come back and buy' creative to people who already bought yesterday. Here's how to wire Shopify's paid-order event to a Meta Custom Audience, set it as an exclusion on every retargeting ad set, and time-box it so buyers flow back in when it's actually worth re-selling to them.
- Meta Ads
- Shopify
- n8n