This Shopify returns automation with n8n turns a messy returns inbox into a clean, trackable process. A hosted return request form verifies the order against the Shopify Admin API, tags the order Return requested, appends the request to a Google Sheets RMA log, and emails the customer an acknowledgment. No returns app subscription, no manual order lookups, and every request recorded in a sheet you own. Build time is about 40 minutes.
Prefer to skip the build? Grab the ready-made template from the CTA below and be running in under 10 minutes.
What it does
Returns are the part of running a store that quietly eats hours. A customer emails “I want to send this back,” and now someone has to find the order, confirm it exists, decide if it qualifies, tag it so fulfillment knows, write the reply, and note it somewhere so nothing slips. Do that thirty times a week and you have lost most of a workday to copy-paste.
This workflow takes the intake off your plate. A customer fills in a short return request form. n8n looks the order up in Shopify to confirm it is real, then tags the matching order with Return requested so it shows up in a saved Shopify view for your fulfillment team. It writes a row to a Google Sheets RMA log with a return number, the reason, and a status of Requested. Finally it emails the customer a friendly acknowledgment with their RMA number and what happens next.
What it deliberately does not do is issue money. Refunds and replacements stay a human decision inside Shopify. The automation removes the busywork around that decision, not the decision itself. This sits in the same family as the refund tracker, which logs refunds after the fact; this one runs at the front of the process, when the request first arrives.
Why it beats the default
The default is one of two things: a shared inbox where return requests get lost between order confirmations, or a paid returns app that charges a monthly fee and still keeps your data locked inside its own dashboard.
A plain inbox has no structure. There is no single place to see every open return, no automatic tagging, and no guarantee the customer even got a reply. Requests fall through when things get busy, and busy is exactly when returns spike.
A dedicated returns app fixes the structure but adds a recurring cost and another login, and the return records live in a system you do not control. If you cancel the app, you often lose easy access to the history.
This n8n approach gives you the structure without the lock-in. Tagging happens automatically, every request lands in a spreadsheet you can filter and export, the customer always gets a same-minute acknowledgment, and the whole thing runs on tools most stores already use. You own the data and the logic end to end. For the wider picture of what you can wire into your store this way, see the n8n Shopify automation guide.
What you need
- An n8n instance (Cloud or self-hosted), version 1.0 or newer.
- A Shopify store with Admin API access. Set this up with the 2026 Dev Dashboard method described in connect Shopify to n8n. The custom app needs
read_ordersandwrite_ordersscopes. - A Google account for Google Sheets, connected in n8n by OAuth2.
- A Gmail account for the customer acknowledgment email, also connected by OAuth2.
- A blank Google Sheet with a header row for the RMA log (columns listed further down).
The workflow uses the Shopify Admin API version 2026-04. No returns app, no third-party service, no blocked messaging channels.
Node-by-node list
Eight nodes, one clean path with a single branch for unmatched orders.
| # | Node | Type | Job |
|---|---|---|---|
| 1 | Return request form | Form Trigger |
Hosted form that collects order number, email, item, and reason |
| 2 | Find the order | HTTP Request (GET) |
Looks up the order in Shopify by name to confirm it exists |
| 3 | Order found? | If |
Routes to the return flow only if an order matched |
| 4 | Build the RMA record | Edit Fields |
Creates the return number and merges the new tag with existing tags |
| 5 | Tag the order | HTTP Request (PUT) |
Writes the merged tag list back to the order in Shopify |
| 6 | Log the request | Google Sheets (Append) |
Adds one row to the RMA log with status Requested |
| 7 | Email the customer | Gmail (Send) |
Sends the acknowledgment with the RMA number |
| 8 | Flag unmatched request | Gmail (Send) |
False branch: alerts support when no order matched |
┌──────────────────────────────────────────────────────────────────┐ │ SHOPIFY RETURNS AUTOMATION (n8n) │ │ │ │ [Return request form] → [Find the order] → [Order found?] │ │ │ true │ │ ▼ │ │ [Build RMA] → [Tag order] → [Sheets log] │ │ │ │ │ ▼ │ │ [Email customer] │ │ │ false │ │ ▼ │ │ [Flag unmatched → support] │ └──────────────────────────────────────────────────────────────────┘
Step-by-step build
1 Return request form (Form Trigger)
Add a Form Trigger node. This gives you a hosted URL you can link from your store. Set the form title to “Request a return” and add these fields: Order number (text, required), Email (email, required), Product name (text), Reason (dropdown: Damaged, Wrong item, Changed mind, Wrong size, Other), and Comments (textarea). When a customer submits, the node outputs one item shaped like this:
{
"Order number": "1042",
"Email": "emily.rodriguez@gmail.com",
"Product name": "Cedar Trail Running Jacket",
"Reason": "Wrong size",
"Comments": "Ordered medium, need large."
}
Ask customers to enter the order number without the #. The Admin API matches on the numeric name, so 1042 is cleaner than #1042.
2 Find the order (HTTP Request, GET)
Add an HTTP Request node set to GET. This confirms the order is real and pulls the existing tags so you do not overwrite them. Point it at your store:
GET https://YOUR-STORE.myshopify.com/admin/api/2026-04/orders.json
?name={{ $json["Order number"] }}&status=any&fields=id,name,email,tags
Use Header Auth for the credential, with header X-Shopify-Access-Token set to your Admin API access token. The response returns an orders array. A match looks like this:
{
"orders": [
{ "id": 5218844733, "name": "#1042", "email": "emily.rodriguez@gmail.com", "tags": "VIP" }
]
}
3 Order found? (If)
Add an If node. Create one condition using the number comparison “is not empty” or “larger than 0” on the array length:
{{ $json.orders.length }} → larger than → 0
The true output carries on to the return flow. The false output goes to the support-alert email in step 8.
4 Build the RMA record (Edit Fields)
Add an Edit Fields (Set) node. This is where you assemble everything the later nodes need. Add four assignments:
order_id(number):{{ $json.orders[0].id }}rma_number(string):{{ "RMA-" + $json.orders[0].name.replace("#","") }}merged_tags(string):{{ ($json.orders[0].tags ? $json.orders[0].tags + ", " : "") + "Return requested" }}customer_email(string):{{ $('Return request form').item.json.Email }}
The merged_tags expression keeps any existing tags (like VIP) and appends Return requested. Sending only the new tag would erase the order’s current tags.
5 Tag the order (HTTP Request, PUT)
Add a second HTTP Request node set to PUT. Send the merged tag list back to the order. In the body section, set Body Content Type to JSON and paste this into the JSON body field:
{
"order": {
"id": {{ $json.order_id }},
"tags": "{{ $json.merged_tags }}"
}
}
PUT https://YOUR-STORE.myshopify.com/admin/api/2026-04/orders/{{ $json.order_id }}.json
Reuse the same Header Auth credential. After this runs, the order in Shopify shows the Return requested tag, and you can build a saved order view filtered on that tag for your team.
6 Log the request (Google Sheets, Append)
Add a Google Sheets node, operation Append. Point it at your RMA log sheet and map the columns to the fields you built. Set Status to the literal Requested and Date to {{ $now.format('yyyy-MM-dd HH:mm') }}. This gives you a single filterable record of every return that has ever come in.
7 Email the customer (Gmail, Send)
Add a Gmail node, Send operation. Set To to {{ $('Build the RMA record').item.json.customer_email }}, a subject like Your return request {{ $('Build the RMA record').item.json.rma_number }}, and a short friendly body confirming you received the request and will follow up with next steps. Same-minute acknowledgment is what stops the “did you get my email?” follow-ups.
8 Flag unmatched request (Gmail, Send)
Connect a second Gmail node to the false output of the If node. Send it to your support address with the submitted order number and email so a person can reach out. This catches typos and orders from a different sales channel without polluting the returns log.
The RMA log columns
| Column | Example | Description |
|---|---|---|
Date |
2026-07-18 14:30 | When the request came in |
RMA number |
RMA-1042 | Reference given to the customer |
Order |
#1042 | The Shopify order name |
Email |
emily.rodriguez@gmail.com | Customer contact |
Product |
Cedar Trail Running Jacket | Item being returned |
Reason |
Wrong size | Why the customer is returning |
Status |
Requested | Requested, Approved, Received, or Refunded |
Common mistakes
- Overwriting tags. The single most common error is sending only
Return requestedin the PUT body, which wipes existing tags. Always merge with the current tags from step 2. - Wrong order lookup field. The Admin API matches the storefront number on
name, notid. Searching byidwith a customer-facing number returns nothing. - Skipping the If node. Without the order-exists check, a typo creates a phantom RMA row and a confusing customer email. The branch keeps your data honest.
- Trusting the form for money. Do not wire an automatic refund onto this. Keep a human between the request and the refund. The tag and the sheet exist so that review is fast, not skipped.
- Forgetting the API scope. A PUT that returns 403 almost always means the app is missing
write_orders. Add the scope and reinstall the app.
Cost at realistic volume
Say your store handles 300 return requests a month, which is a healthy volume for a mid-size store.
- Shopify Admin API: free. Each request makes two calls (one GET, one PUT), so 600 calls a month, far under the rate limits.
- Google Sheets and Gmail: free at this scale on a standard Google account. 300 appends and 300 emails is nowhere near any cap.
- n8n: free on self-hosted. On n8n Cloud, 300 runs a month sits comfortably inside the Starter execution allowance.
So the running cost is effectively zero, versus a dedicated returns app that typically starts around ten to thirty dollars a month and scales up with volume. Over a year that is a few hundred dollars saved, plus the hours you get back from not doing manual order lookups.
🚀 Get the Shopify returns automation template
The guide above is free to follow. If you would rather skip the build, download the ready-to-import workflow, then just add your credentials. Prefer it installed and tuned for you? See our done-for-you services.
Instant download · Works on n8n Cloud and self-hosted
Frequently asked questions
Does this Shopify returns automation approve or reject refunds?
No. It handles the intake side: it captures the request, confirms the order is real, tags the order, logs it, and emails the customer. A human reviews each tagged order and issues the refund or replacement inside Shopify, so money decisions stay with a person.
Do I need a paid Shopify returns app for this?
No. The workflow uses the standard Shopify Admin API to look up and tag orders, plus an n8n form, Google Sheets, and Gmail. You avoid the monthly app fee and keep every return record in a spreadsheet you own and can export at any time.
How does the customer submit a return request?
The n8n Form Trigger gives you a hosted URL you link from your store footer, order confirmation email, or a help page. The customer enters their order number, email, item, and reason. No account or login is required, so it takes under a minute.
What happens if the order number does not match?
An If node checks whether the Admin API returned an order. If none matched, the false branch emails your support inbox so a person can follow up. Nothing gets tagged or logged as a valid return, which keeps your returns sheet clean and trustworthy.
Can I route returns to different teams by reason?
Yes. Swap the single If node for a Switch node keyed on the reason field. Damaged items can email your warehouse, wrong-item requests can go to fulfillment, and change-of-mind requests can go to support. The rest of the tag-log-email chain stays the same.