The sale is the easy part. The harder part is the silence right after it, when a new customer is waiting for a package and quietly deciding whether your store was a one-time thing or somewhere they will come back to. Most stores fill that silence with nothing, or with a generic “thanks for your order” that fires the second the checkout completes and is forgotten by the time the box arrives.
A Shopify post-purchase follow-up email built in n8n closes that gap. This workflow waits a few days after each order is fulfilled, drafts a short personalized line about what the customer actually bought, and sends a warm follow-up through your own Gmail account. It costs almost nothing to run and it asks for the review, the reorder, or the reply at the one moment the customer is most likely to give it.
Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.
TL;DR
This guide builds a Shopify post-purchase follow-up email in n8n that runs on a daily schedule, pulls orders fulfilled a set number of days ago from Shopify, uses OpenAI to write one friendly personalized sentence about the product, and sends the email through Gmail. It needs no paid apps, costs a few cents a month, and turns the dead air after delivery into reviews, replies, and repeat orders. Build it from scratch or import the template.
What it does
Every morning the workflow asks Shopify a simple question: which orders were fulfilled exactly N days ago? For each one it pulls the customer’s name, email, order number, and the first product they bought. It hands that to OpenAI to write a single natural sentence that references the actual product, drops that sentence into a clean email template, and sends it from your Gmail address.
The customer receives something that reads like you typed it personally a few days after their package landed: a quick check that everything arrived well, a gentle nudge to leave a review, and an open door to reply. No app to install on the storefront, no third-party email platform, no monthly seat fee.
Why it beats the default
Shopify’s built-in order confirmation and most email apps fire on a trigger the instant the order is placed. That is the wrong moment for a relationship email. The customer has not received anything yet, has nothing to react to, and treats the message as a receipt.
Timing the email to fulfillment plus a few days changes the whole tone. The package has arrived, the customer has formed an opinion, and a short personal note lands as thoughtful rather than automated. Three more reasons this approach wins:
- It runs from your own Gmail, so replies come straight back to your inbox and the email inherits your existing sender reputation instead of a shared app domain.
- The personalization is real. Referencing the exact product the customer bought reads completely differently from “Hi {{first_name}}, thanks for shopping with us.”
- You own every line. There is no plan to upgrade, no send limit, and no vendor sitting between you and your customer list.
If you are mapping out a wider automation stack, this fits alongside the other recipes in our n8n Shopify automation guide.
What you need
- An n8n instance (Cloud or self-hosted), version 1.0 or newer.
- A Shopify store with admin access to create a custom app and an Admin API access token.
- A Gmail account connected to n8n through Gmail OAuth2.
- An OpenAI API key on pay-as-you-go billing (optional — you can hardcode a line instead).
Estimated build time: 30–45 minutes from scratch, or under 10 minutes with the template.
How it works — the big picture
┌──────────────────────────────────────────────────────────────┐ │ SHOPIFY POST-PURCHASE FOLLOW-UP │ │ │ │ [Schedule 9am] → [Shopify: get orders] → [Code: N days ago] │ │ ↓ │ │ [OpenAI: write line] → [Edit Fields] │ │ ↓ │ │ [Gmail: send] │ └──────────────────────────────────────────────────────────────┘
Node-by-node list
| # | Node | Type | Job |
|---|---|---|---|
| 1 | Schedule Trigger | scheduleTrigger |
Runs the workflow once a day at 9am |
| 2 | Get Shopify Orders | shopify |
Fetches shipped orders updated in the last several days |
| 3 | Keep N-Day-Old Orders | code |
Keeps only orders fulfilled exactly N days ago |
| 4 | Write Follow-Up Line | @n8n/n8n-nodes-langchain.openAi |
Drafts one personalized sentence about the product |
| 5 | Build Email | set |
Assembles recipient, subject, and HTML body |
| 6 | Send Follow-Up | gmail |
Sends the email from your Gmail account |
Step-by-step build
1 Schedule Trigger (scheduleTrigger)
This is the entry point. It wakes the workflow once a day so you check for newly arrived orders on a steady cadence.
- Add a Schedule Trigger node.
- Set the interval to Days, every
1day. - Set the trigger hour to
9(9am in your instance timezone).
Tip: Pick a mid-morning hour in your customers’ main timezone. Follow-up emails opened over a morning coffee get more replies than ones that land at 3am.
2 Get Shopify Orders (shopify)
This pulls a manageable batch of recent orders that you will narrow down in the next step.
- Add a Shopify node, resource Order, operation Get Many.
- Turn Return All on (or set a high limit).
- Under Filters, set Fulfillment Status to
shippedand Updated At Min to={{ $now.minus({ days: 10 }).toISO() }}.
Pulling a 10-day window keeps the batch small while making sure the order you want (fulfilled around 5 days ago) is always inside it. A typical order item arrives looking like this:
{
"order_number": 1042,
"email": "emily.rodriguez@gmail.com",
"customer": { "first_name": "Emily", "last_name": "Rodriguez" },
"line_items": [{ "title": "Cedar & Sage Soy Candle" }],
"fulfillments": [{ "created_at": "2026-06-10T14:30:00-04:00" }]
}
3 Keep N-Day-Old Orders (code)
This is the heart of the timing logic. It keeps only the orders whose fulfillment happened exactly N days ago, so each order is contacted on one day and one day only.
- Add a Code node, mode Run Once for All Items.
- Paste the script below and set
DAYS_AFTERto your preferred window.
const DAYS_AFTER = 5;
const out = [];
for (const item of $input.all()) {
const order = item.json;
const fulfillment = (order.fulfillments || [])[0];
if (!fulfillment || !order.email) continue;
const shipped = new Date(fulfillment.created_at);
const days = Math.floor((Date.now() - shipped.getTime()) / 86400000);
if (days !== DAYS_AFTER) continue;
out.push({
json: {
email: order.email,
first_name: order.customer?.first_name || "there",
order_number: order.order_number,
product: order.line_items?.[0]?.title || "your order"
}
});
}
return out;
Tip: Test with DAYS_AFTER = 0 first so you can place a test order, fulfill it, and see the email the same day instead of waiting five days to confirm it works.
4 Write Follow-Up Line (OpenAI)
This generates one short, natural sentence that mentions the product by name, so the email never reads like a mail merge.
- Add an OpenAI node, resource Text, operation Message a Model.
- Pick a small, cheap model such as
gpt-4o-mini. - Add a user message with this prompt:
Write one warm, casual sentence (max 20 words) checking in with a customer
named {{ $json.first_name }} about their recent purchase of
"{{ $json.product }}". No greeting, no sign-off, just the sentence.
The model returns something like: “Hope the Cedar & Sage candle has been filling the room nicely since it arrived!”
Skip OpenAI entirely if you prefer zero cost — just write one fixed friendly line directly in the next node. The workflow still works exactly the same.
5 Build Email (set)
This assembles the three pieces Gmail needs: who it goes to, the subject, and the HTML body. Use the Edit Fields node (Set v3.4) with one assignment per field.
- Add an Edit Fields node.
- Add a string field
to=={{ $('Keep N-Day-Old Orders').item.json.email }}. - Add a string field
subject==Quick check-in on order #{{ $('Keep N-Day-Old Orders').item.json.order_number }}. - Add a string field
htmlwith the body below.
Hi {{ $('Keep N-Day-Old Orders').item.json.first_name }},<br><br>
{{ $json.message.content }}<br><br>
If everything's good, a quick review would mean a lot. And if anything's
off, just reply to this email — it comes straight to me.<br><br>
Thanks again,<br>The team
Tip: Keep the body plain and short. A follow-up that looks like a personal email out-performs a heavily designed template, because it invites a reply instead of a click.
6 Send Follow-Up (gmail)
The final node sends the message from your connected Gmail account.
- Add a Gmail node, operation Send.
- To:
={{ $json.to }} - Subject:
={{ $json.subject }} - Message:
={{ $json.html }}, and set Email Type to HTML. - Save the workflow and toggle it Active.
Common mistakes
| Problem | Likely cause | Fix |
|---|---|---|
| No emails ever send | No order was fulfilled exactly N days ago today | Set DAYS_AFTER = 0 and test with a freshly fulfilled order |
| Same customer emailed twice | The follow-up window was widened beyond one day | Keep the exact-match filter, or log sent order numbers to a sheet |
| Email body shows raw expressions | Gmail Email Type left as Text | Switch the Gmail node Email Type to HTML |
| Personalized line is missing | Wrong field referenced from the OpenAI node | Use {{ $json.message.content }} in the body |
| Orders never appear | Fulfillment Status filter set wrong | Confirm it is shipped and orders are actually fulfilled in Shopify |
Cost at realistic volume
The only paid piece is the OpenAI call, and it is tiny. Each follow-up uses roughly 150 tokens in and 30 tokens out on a small model — well under a hundredth of a cent per email.
| Orders / month | OpenAI cost | Gmail | n8n |
|---|---|---|---|
| 100 | ~$0.02 | Free | Free (self-hosted) |
| 500 | ~$0.10 | Free | Free (self-hosted) |
| 2,000 | ~$0.40 | Free* | Free (self-hosted) |
*Gmail’s free tier sends up to 500 messages per day, which covers most small and mid-size stores. Above that, a Google Workspace account raises the limit to 2,000 per day. Drop the OpenAI node and the running cost is effectively zero.
🚀 Get the Shopify Post-Purchase Follow-Up Template
Skip the build. The ready-made template imports in minutes, with the timing logic, AI prompt, and email layout already wired — or let us set the whole thing up for you.
See our done-for-you services →
Instant import · Works on n8n Cloud and self-hosted
Frequently asked questions
How many days after fulfillment should the follow-up email go out?
Five to seven days after the order ships works for most stores, because the package has usually arrived by then. Set the number in one place inside the Code node, so you can test a few values and keep whichever earns the best reply and review rate for your products.
Do I need a paid OpenAI plan for this workflow?
No. The personalization call is tiny, so even at a few hundred orders a month the cost is a few cents on a pay-as-you-go OpenAI key. If you would rather spend nothing, you can delete the OpenAI node and write one fixed friendly line in the Edit Fields node instead.
Will this send duplicate emails to the same customer?
No, as long as the Code node matches a single fulfillment date. Because it only keeps orders fulfilled exactly N days ago, each order passes the filter on one day only. If you widen the window, add a Google Sheets check to record which order numbers were already contacted.
Can I use Outlook or plain SMTP instead of Gmail?
Yes. Swap the Gmail node for the Microsoft Outlook node or the Send Email (SMTP) node. The fields you built in the Edit Fields step (recipient, subject, body) map directly, so the rest of the workflow does not change at all.
What if a customer already left a review or asked for a refund?
Add an IF node after the Code node to skip orders with a refund or cancellation, using the financial status from the Shopify order. For reviews, the cleanest approach is to log contacted orders to a sheet and exclude anyone who already replied or reviewed.