The original manual process ? about a day every week
Before any of this was automated, the out-of-stock review process at Army & Outdoors looked like this:
- Export the full out-of-stock product list from Shopify (CSV)
- Open it in Excel and run formulas to consolidate ? Shopify exports variants individually, so I had to find products where all variants were out of stock (or single-variant products that were out)
- For each consolidated OOS product, cross-reference the stock purchasing Google Sheet to see if it was coming back
- If coming back: add to a "keep list" with a date, manually skip it in next week's consolidation formula
- If not coming back: add to a "review" list to send to the purchasing manager
- Email the list to the purchasing manager
- Wait for him to reply with decisions: source from elsewhere / hide the product / something else
- For each product flagged "hide": go into Shopify, unpublish it, and manually create a 301 redirect to a sensible alternative
- Repeat for the AU store. And the US store. And the EU store.
Total time: over a full working day, every single week. And the moment I missed a Monday, the OOS backlog grew. Customers were landing on dead product pages.
The plan ? automate the data work, keep the human decisions
I sat down with my manager and walked through the flow. Most of the steps were mechanical ? exporting data, consolidating, cross-referencing, emailing. The genuinely human steps were narrow: the purchasing manager's decision on each product, and the choice of where to redirect each hidden product.
The plan: automate everything that's mechanical. Surface only the decisions that need a human.
The build ? 2 cron jobs + a custom decision app
Cron job 1 ? daily OOS scanner
- Fetches out-of-stock products from Shopify across all 4 regional stores
- Consolidates correctly (product is OOS only when ALL variants are OOS, or a single-variant product is OOS)
- Stores the result in a MySQL table with timestamps
- Cross-checks against the keep-list table ? if a product is on the keep list with a recent date, it gets skipped from the OOS queue
Cron job 2 ? Monday morning matching + email
- Pulls the latest OOS list from the database
- Cross-references each product against the stock purchasing Google Sheet via the Google Sheets API
- If the sheet says "coming back": removes from OOS list, adds to keep list with current date
- If NOT coming back, OR if the product has been sitting on the keep list for 30+ days without arriving: it's promoted to the "review" list
- At 9am every Monday, the system auto-emails the consolidated review list to the purchasing manager and stores a snapshot in a separate table for audit
The custom decision app ? where the human work happens
The purchasing manager reviews the email and replies with his decisions. I open a custom in-house app that shows the same list as a sortable data table:
- Products the manager said to keep ? I tick a checkbox per row, click "Save to keep list" ? those products move to the keep list table for 30 days
- Products to hide ? I add the redirect target URL for each (usually the parent collection or a similar product)
- Click "Hide and redirect" per product
What the "Hide and redirect" button actually does
One click triggers a coordinated series of API calls across all 4 stores:
- Unpublishes the product from every active sales channel ? NZ, AU, US, EU
- Creates a 301 redirect from the product URL to the chosen target ? in every store
- Critically: also creates redirects for the alternate Shopify URL formats. Shopify generates multiple URL paths for the same product depending on how it was navigated to. For example a product can be reached via
/products/abcAND/collections/tr/products/abcAND/collections/category/products/abc. All these need redirecting separately or you end up with 404s deep in the navigation
The result
- The full-day weekly process is now ~15 minutes on Monday morning ? open the email, open the app, click through the manager's decisions
- Zero missed weeks since deploy ? the cron runs whether I'm there or not
- 4 stores stay in sync automatically ? what's hidden in NZ is also hidden in AU, US, EU at the same time, with the same redirects in place
- No more dead product pages ? every hidden product redirects somewhere useful, including the alternate URL paths Shopify quietly generates
- Audit trail ? every Monday's review list is snapshotted in MySQL, so we can see historical decisions if anyone asks
Why this matters for your store
If your team has a recurring data + decision workflow that takes hours every week, there's almost always a way to split it: the boring data work (collection, consolidation, cross-referencing) is mechanical and automatable. The judgement work (what to do about it) is human and shouldn't be automated.
Most agencies will quote you "an automation project" that tries to automate the human decisions too ? which produces brittle workflows that fail in ambiguous cases. The more useful pattern is: automate the data, surface the decisions, give the human a fast tool to act on them.
That's exactly the pattern I built here, and it's been running quietly for years. Same pattern can apply to any recurring multi-store catalogue management work ? discontinuations, price audits, compliance reviews, anything where a human needs to make calls but shouldn't be doing the data prep.