Recipe: nightly cleanup with Repeat for each
Goal: every night, archive open orders that are paid and fulfilled and older than 14 days.
Template: Archive completed orders every night (category Maintenance). Steps: 4. Needs: access to orders, and Shopify's approval for protected customer data.
The same four parts make up almost every scheduled cleanup: a schedule, a query that finds what is to do, a loop that does it, a log line that sums it up.
Create it
In Browse templates, search for archive and select Use template on Archive completed orders every night.
How it is built
| Step | Setting |
|---|---|
| Trigger | On a schedule, cron 0 2 * * *: every night at 02:00 in the chosen time zone |
| Find completed orders (Shopify query) | The first 50 open, paid and shipped orders created before the date 14 days ago |
| For each order (Repeat for each) | List {{ steps.orders.output.orders.nodes }}, When an item fails: Go on with the next item |
| Archive the order (Archive order) | Order id {{ loop.item.id }} |
| After the loop: Log | How many were archived |
The query's Variables (JSON) build the search, with the date worked out in Liquid:
{ "q": "status:open financial_status:paid fulfillment_status:shipped created_at:<{{ 'now' | date: '%s' | minus: 1209600 | date: '%Y-%m-%d' }}" }
1,209,600 is 14 days in seconds. The log message:
Archived {{ steps.each_order.output.succeeded }} of {{ steps.each_order.output.count }} orders
Make it yours
- Select the trigger. Set the time under Cron expression and choose your Time zone. See Schedules.
- Select the query step and change the age: replace
1209600with your number of days times 86,400. - Test as a preview. The query runs for real, so you see which orders it finds. The archive steps are previewed for each of them: nothing is changed.
- Save and Turn on.
Why 50 per run
A query step reads one page of results, here 50 orders. That is on purpose: the job finishes quickly, and what is left over is found again the next night, because archived orders no longer match status:open. A job that removes its own work from the search can never fall behind for long, and it can run twice without harm.
If you have more than 50 such orders per night, let it run more often, for example every hour at night with 0 0-5 * * *, or raise first in the query. Mind the limits: a Repeat for each takes up to 500 items, 100 unless you raise Maximum items, and one run executes up to 1,000 steps. See Repeat for each.
Why go on with the next item
With Go on with the next item, one order that cannot be archived does not stop the others. The log line shows it: 49 of 50. Open the run in the Run history to see which item failed and why.
If you would rather be alerted, add a Condition after the loop on steps.each_order.output.failed is greater than 0, and behind it a Fail the run step with a message. The run then ends as Failed and sends the failure alert. See Wait, Log and Fail the run and Alerts by email and text message.
The same pattern, other jobs
| Template | Finds | Does |
|---|---|---|
| Delete old draft orders every week | Draft orders older than a number of days | Deletes each one |
| Remove the new tag from older products | Products that still carry the tag and are older than 30 days | Removes the tag |
| Weekly check for products without images | Active products without an image | Tags each one |
| Tag all products of a vendor with one click | The products of one vendor, started with Run now | Adds a tag |
Each of them reads one page per run. Every run counts as one run of your plan, however many items it handles.

