automations.help

Order & fulfillment ops

Auto-cancel and refund orders unfulfilled after 14 days

Some orders are never going to ship — the item's gone, the supplier fell through, the address was bad. They sit open, tying up cash and phantom inventory while the customer waits. This finds them past your cutoff, refunds the Stripe charge, restocks the units, and emails a clear explanation — with guardrails so it never touches an order that's mid-fulfillment. Build it yourself with the steps below — or we build it, wire in the safeguards, and hand it over so you own it outright with no monthly fee. Free teardown of your current setup first.

Difficulty
Intermediate
Time to build
3-6 hrs
Updated
  • Shopify
  • Stripe

The manual way today

Every so often someone filters the Orders list to 'unfulfilled', eyeballs the dates, and tries to remember which ones are real stragglers versus preorders or backorders. For each genuine straggler they refund the payment, manually restock the items, write the customer an apology, and cancel the order — one at a time, from memory, whenever they happen to think of it.

Money the customer paid weeks ago sits on your books as a sale you can't ship, and the units stay 'committed' so your available stock reads low when it isn't. The longer a paid-but-unshipped order goes quiet, the more likely it ends in a chargeback or a furious ticket — and chargebacks cost you the fee on top of the refund. None of that shows up until you tally it.

What running looks like

Once a day, any fully-unfulfilled paid order past your cutoff is refunded to the original payment method, its inventory is restocked, the order is cancelled with a logged reason, and the customer gets a plain email explaining what happened. Partial shipments and held orders are never auto-touched — they're flagged to a person instead.

01The build

How to build it

  1. Write down the cutoff and the exceptions first

    Needs a human

    Decide the window — e.g. 14 days from the paid date, not the order date — and list everything that's exempt: preorders, backorders, made-to-order items, subscription orders, and anything carrying a 'hold' or 'awaiting stock' tag. This policy is a human decision; the automation just enforces it.

  2. Query Shopify daily for stale unfulfilled orders

    Runs itself

    On a once-a-day schedule (Shopify Flow's scheduled trigger, or a cron in n8n/Zapier hitting the GraphQL Admin API), pull orders matching financial_status:paid, fulfillment_status:unfulfilled, and status:open — explicitly not 'partial', since a partially shipped order is never safe to auto-cancel. Order search can't filter on the paid date directly, so compute each order's age from its processed_at (or, for precision, the date of the SUCCESS sale/capture transaction) and keep only the ones past your cutoff.

  3. Filter out anything that shouldn't be touched

    Runs itself

    Drop orders that carry your hold/preorder/backorder tags, have a fulfillment hold applied, or have a fulfillment already in progress. The rule is simple and one-directional: when in doubt, skip and flag — never cancel on a guess. This is the safeguard that keeps a mid-fulfillment order from getting wiped.

  4. Refund the payment

    Runs itself

    For each qualifying order, issue a full refund (item totals, tax, and shipping). The clean path is Shopify's refund API — calculate the refund, then create it — which reverses the underlying Stripe charge and records the refund on the order. If you charge Stripe directly outside Shopify, call Stripe's Refund API with the order's PaymentIntent ID instead.

  5. Restock the inventory in the same step

    Runs itself

    Set restock to true on the refund (restockType CANCEL) so the committed units return to available immediately. If your inventory lives somewhere other than Shopify, write the quantities back to that source of truth so the counts don't drift.

  6. Cancel the order with a reason and a note

    Runs itself

    Cancel the order with the orderCancel mutation: set reason to INVENTORY (or OTHER) and a staffNote like 'Auto-cancelled: unfulfilled 14+ days, refunded in full'. Cancel it — don't delete it; you want the record for reporting and the customer's account history. Shortcut: when you're issuing a full refund, current API versions let orderCancel do everything in one mutation — refund: true, restock: true, notifyCustomer: true (which fires the cancellation email from the next step) plus the staffNote — so steps 4–6 collapse into a single call. Keep them split only when you need partial-refund control or you charge Stripe outside Shopify.

  7. Email the customer a plain explanation

    Runs itself

    Send a specific, no-spin message: we weren't able to fulfill this order, you've been refunded the full amount to your original payment method, here's how much and roughly when it'll land, and here's how to reorder or reach a human. Customize Shopify's order-cancelled notification, or send it from your ESP.

  8. Log every action and route the misfits to a person

    Needs a human

    Append each refund + cancel to a sheet or a Slack channel for your records, and send anything the filter skipped — partial shipments, mixed-status orders, manual-review tags — to a human to resolve by hand. The automation handles the clean cases; people handle the judgment calls.

02The stack

What it’s built on

Shopify
System of record for the order, payment link, and inventory. Its refund API reverses the charge and restocks in a single call, it cancels the order with a logged reason, and it sends the customer's cancellation email. Shopify Flow's scheduled trigger can also run the whole thing natively.
Stripe
The processor that actually returns the money to the customer's card.If you take payments through Shopify Payments, that's Stripe under the hood — the Shopify refund reverses the Stripe charge for you and you usually never touch Stripe's API directly. You only call Stripe yourself if you charge it outside Shopify.

03Questions

Before you build

Will this ever cancel an order that's actually being fulfilled?

No — not if you keep the guardrails. It only processes orders that are still fully unfulfilled, have no fulfillment hold, and don't carry your hold/preorder tags. Anything partial or mid-fulfillment is skipped and flagged for a person, never auto-cancelled.

Do I get the payment processing fee back when I refund?

Usually no. On Shopify Payments and most Stripe accounts the original processing fee isn't returned on a refund, so a full refund still costs you that fee. It's almost always the cheaper outcome anyway — let the order rot into a chargeback and you pay the refund, the lost fee, and a dispute fee on top. Confirm the exact policy with your processor.

Does the refund go back to the original card automatically?

Yes. Shopify's refund API (or Stripe's, if you charge directly) returns the money to the original payment method. You don't issue store credit or a manual transfer unless you choose to — and for an order you simply can't ship, a real refund is the honest default.

What about orders that already shipped part of the items?

Those are deliberately excluded. Auto-refunding and cancelling an order that's partially out the door is how you end up refunding shipped product. Partial orders are routed to a human to handle case by case.

Can I notify the customer and hold the order instead of cancelling?

Yes, and for slow-but-coming stock that's often the better move — a delay notice keeps the sale. This recipe is for orders you genuinely can't fulfill. If you mostly need to buy time, build a fulfillment-delay notification flow instead.

Reporting & dashboards

Track refund rate and reasons in a dashboard

Your refunds line goes up and all you can see is the dollar total — not what percentage of orders it is, whether it's getting worse, or which products and reasons are driving it. This wires the answer up: a Google Apps Script pulls every refund and its return reason from the Shopify Admin API into a logged-and-normalized refunds tab, alongside an orders tab for the denominator, and a dashboard tab built on QUERY formulas shows refund rate over time, reasons ranked, and the products doing the returning — recalculating on its own as new rows land. Build it yourself with the steps below, or we build it on your own Shopify and Google accounts, reconcile it against your Shopify reports, and hand you the keys so you own it with no monthly fee. Free teardown of your current refund reporting first.

  • Shopify
  • Google Sheets

Intermediate3-5 hrs

Support & returns

Auto-issue store credit for returns

Every approved return that resolves as store credit ends the same way: an agent works out the amount, opens the customer up, and manually issues a gift card or credit — then writes the 'here's your balance' reply. It's small, it's identical every time, and it happens on every single case. This wires the credit to the return itself: when the package is back and the return is closed, Shopify credits the customer's store-credit account for the right amount and emails them — untouched. Build it yourself with the steps below — or we build it, wire in the guardrails, and hand it over so you own it outright with no monthly fee. Free teardown of your current returns flow first.

  • Shopify
  • Gorgias

Intermediate3-6 hrs