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
The manual way today
Maybe once a week someone exports recent orders from Shopify, dumps the emails into a CSV, uploads it to a Meta Custom Audience, and checks that audience is still set as an exclusion on the retargeting ad sets. Between uploads, every customer who bought is still sitting in the 'viewed product / added to cart' retargeting pool the moment they convert.
A weekly upload means up to seven days where you're paying to retarget people who already paid — the worst impressions you can buy, on your most expensive (warmest) audience. It also annoys the customer who just bought and now gets chased by a discount ad for the thing already shipping to them, and it quietly inflates your retargeting frequency so the numbers look like the audience is fatiguing when it's really just full of buyers.
What running looks like
A paid order in Shopify adds that customer to a 'recent purchasers — suppress' Custom Audience in Meta within a minute, and that audience is set as an exclusion on every retargeting ad set — so buyers stop seeing acquisition retargeting almost immediately. A daily job removes them once the suppression window passes, so they re-enter retargeting (or a win-back flow) exactly when re-selling makes sense again. Nobody touches a CSV.
01The build
How to build it
Create the 'recent purchasers — suppress' Custom Audience
Needs a humanIn Meta Ads Manager, create a Custom Audience of type Customer List and leave it empty — this is the suppression bucket, not a targeting audience. Copy its audience ID; that's what the API writes to. Name it so future-you knows it's an exclusion list (e.g. 'SUPPRESS — bought in last 30d') and not something you'd ever accidentally target.
Catch paid orders from Shopify
Runs itselfRegister the Shopify orders/paid webhook (Settings → Notifications, or via the Admin API) pointing at an n8n webhook node. orders/paid fires on actual payment, not order creation, so you don't suppress someone whose checkout never captured. From the payload, grab the customer's email and phone plus the order's processed_at timestamp.
Normalize and SHA-256 hash the identifiers
Runs itselfMeta only accepts hashed PII for customer-list audiences, and the match rate collapses if the normalization is off. Email: trim whitespace and lowercase it. Phone: strip everything but digits and prepend the country code (e.g. 13105551234). Then SHA-256 each value to a lowercase hex string in an n8n Crypto node. Send both EMAIL_SHA256 and PHONE_SHA256 — two keys beats one for match rate.
Add the hashed customer to the suppression audience
Runs itselfPOST to the Marketing API at /{AUDIENCE_ID}/users with schema ["EMAIL_SHA256","PHONE_SHA256"] and a data array of [emailHash, phoneHash] rows. You can batch up to ~10,000 rows per call, but for a per-order webhook you're sending one row. Respect marketing consent: only push customers who accepted marketing (Shopify's accepts_marketing / consent fields), so the suppression list stays clean and defensible.
Set the audience as an exclusion on every retargeting ad set
Needs a humanThis is the step that actually saves the money, and it's a one-time config Meta won't do for you: open each retargeting ad set (website visitors, view-content, add-to-cart, IG/FB engagers) and add the suppression audience under Excluded audiences. Make it part of your ad-set checklist so new retargeting campaigns inherit it. Without this, the audience fills up and changes nothing.
Time-box it: remove buyers after the suppression window
Runs itselfRun a daily n8n Schedule node that re-queries Shopify for orders whose processed_at is now just past your window (say 30 days for a one-time product, or your replenishment cycle for a consumable), hashes those customers, and DELETEs them from the audience via the same /{AUDIENCE_ID}/users endpoint. That's what lets a past buyer flow back into retargeting — or into a win-back flow — instead of being suppressed forever.
Guard the removal: don't pull someone who just bought again
Runs itselfBefore removing a customer in the daily job, confirm they have no other paid order inside the window — otherwise a repeat buyer whose first order aged out gets un-suppressed while their second order is still fresh. Also reset the clock on subscription renewals (Recharge/Shopify subscription orders) so active subscribers don't get retargeted between charges.
Verify the match rate and that the exclusion is live
Needs a humanMeta takes a few hours to match and populate a customer-list audience, so don't panic at an empty count on day one. After a day, check the audience size is climbing and the match rate is reasonable (low match rate usually means a hashing/normalization bug, not bad data). Confirm in Ads Manager that the exclusion shows on each retargeting ad set — a fast spot-check that catches a campaign someone duplicated without it.
02The stack
What it’s built on
- Shopify
- Source of truth for who bought and when. Its orders/paid webhook triggers the add, and a daily re-query of processed_at drives the time-boxed removal.
- Meta Ads
- Holds the suppression Custom Audience and applies it as an exclusion on your retargeting ad sets. The Marketing API's /{audience_id}/users endpoint is what you add and remove hashed customers through.No special tier needed — customer-list Custom Audiences and the exclusion field are standard in any Meta ad account. You only need a system user token with ads_management access to call the API; nothing to buy.
- n8n
- The glue: catches the webhook, normalizes and hashes the identifiers, calls the Marketing API to add the buyer, and runs the daily schedule that removes them after the window.n8n is open-source and self-hostable for free; their cloud tier just saves you running it yourself. Hashing customer PII on infrastructure you control (self-hosted) is the cleaner privacy story.
03Questions
Before you build
Does this actually save money, or just move people between audiences?
It saves money, but only because of step five. Adding buyers to an audience does nothing on its own — the spend stops when that audience is set as an exclusion on your retargeting ad sets, so Meta stops serving warm-audience impressions to people who already converted. The audience-building is just what keeps that exclusion current automatically instead of a weekly CSV.
Do I need customer consent to upload emails to Meta?
Treat it like any other PII you share with Meta. The clean approach is to only push customers who accepted marketing (Shopify tracks this), honor deletion and opt-out requests by removing them from the audience, and check your regional rules — GDPR/CCPA expectations are stricter. Suppression is generally a defensible use, but 'only consented customers' keeps the list clean and keeps you out of trouble.
How long until buyers actually drop out of my ads?
The add is near-instant, but Meta takes a few hours to match a hashed record and populate the audience, and ad delivery updates on its own cadence after that. So expect 'within hours,' not 'within seconds.' That's still a world better than a once-a-week manual upload that leaves buyers in the pool for days.
Won't this block people I actually want to sell to again?
Only inside the window you set, which is the point of the time-box in step six. Pick a window that matches the product: ~30 days for a one-time purchase, or the replenishment cycle for a consumable. When it lapses, the daily job removes them, and they re-enter retargeting or a win-back flow — so you're not suppressing a 90-day-lapsed customer who's now worth re-acquiring.
What about refunds or someone who returns everything?
That's a decision worth making on purpose, not a bug. If you want returned customers back in your acquisition retargeting, add a small Shopify refunds/cancelled branch that removes them from the suppression audience. If you'd rather not chase someone who just returned, leave them in until the window lapses. Either is valid — just pick one so it's not accidental.
Can I build this myself, or should you build it?
Every webhook, field, and the exact hashing normalization is spelled out above, so if you've got API comfort and an afternoon, build it. If you'd rather not own a customer-PII flow and a daily removal job, we'll build it on the Shopify, Meta, and n8n accounts you already use, document it, and hand you the keys — you own the workflow outright, no monthly retainer. It starts with a free 20-minute teardown, not a contract.
Related recipes
Lifecycle & marketing
Sync Shopify customers to a Meta Custom Audience
Your customer list lives in Shopify and your targeting lives in Meta, and the only thing connecting them is a CSV someone exports and re-uploads every few weeks. Here's how to wire Shopify customers to a Meta Custom Audience that stays current on its own — backfilling your existing list, adding new buyers, and (the part a CSV never does) removing people when they unsubscribe or get deleted.
- Shopify
- Meta Ads
- n8n
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
Reporting & dashboards
Automate a daily ad spend vs revenue report
To know if yesterday's ad spend paid off, someone opens Ads Manager for the spend, opens Shopify for the revenue, and does the division in their head — across two timezones and two definitions of a 'sale.' This does it for you. Once a day it pulls yesterday's spend from Meta, yesterday's revenue from Shopify, lines the two day-windows up so they're actually comparable, computes blended ROAS (and shows Meta's own attributed ROAS next to it so the gap is visible), and posts one clean line to Slack. Build it yourself from the steps below, or we build it on your own accounts, reconcile the windows and the currency, and hand you the keys so you own it with no monthly fee. Free teardown of how you check whether ads are working first.
- Meta Ads
- Shopify
- Slack
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