Skip to Content
SimulatorsShopifyOrder history window

Simulators

Order history window

The order history window is Shopify’s rule that an Admin app holding read_orders but not read_all_orders reads only orders created or processed in the last 60 days. The simulator applies it on its own clock, so you can test an app that loses sight of old orders. The window holds for orders, order(id:) and ordersCount alike. Load the fulfillment desk scenario, age its orders out of the window and read them with and without the grant:

ADMIN="$RYSTIC_URL/admin/api/2026-10/graphql.json"
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"shopify_fulfillment_desk"}'
curl -s "$ADMIN" -H "X-Shopify-Access-Token: shpat_placeholder" -H "Content-Type: application/json" -d '{"query": "{ ordersCount { count } }"}' | jq -c '.data.ordersCount'
# -> {"count":5}
curl -s -X POST "$RYSTIC_URL/_rystic/clock" -d '{"advance_seconds": 7776000}'
curl -s "$ADMIN" -H "X-Shopify-Access-Token: shpat_placeholder" -H "Content-Type: application/json" -d '{"query": "{ ordersCount { count } }"}' | jq -c '.data.ordersCount'
# -> {"count":0}
curl -s -X POST "$RYSTIC_URL/_rystic/credentials" -d '{"bindings": {"shpat_history": {"actor": "member", "grants": ["read_orders", "read_all_orders"]}}}'
curl -s "$ADMIN" -H "X-Shopify-Access-Token: shpat_history" -H "Content-Type: application/json" -d '{"query": "{ ordersCount { count } }"}' | jq -c '.data.ordersCount'
# -> {"count":5}

Which orders the window hides

An order is outside the window when its createdAt and its processedAt are both more than 60 days before the simulator’s clock. A hidden order is absent from orders and ordersCount, and order(id:) returns null for it, as for an id that does not exist. The window moves with the clock: POST /_rystic/clock and a scenario’s own clock change which orders a token sees. The orders the scenarios seed are dated within 60 days of their clock, so each token sees all of them at boot.

Grants

A grant is one of the Admin access scopes the simulator knows:

  • read_orders and write_orders
  • read_all_orders
  • read_assigned_fulfillment_orders and write_assigned_fulfillment_orders
  • read_merchant_managed_fulfillment_orders and write_merchant_managed_fulfillment_orders
  • read_third_party_fulfillment_orders and write_third_party_fulfillment_orders

Only read_all_orders changes a response: the window is the one rule that reads a grant.

POST /_rystic/credentials binds tokens to an actor and its grants as {"bindings": {"<token>": {"actor": "member", "grants": [...]}}}, and returns 204 with no body. Each call replaces every earlier binding, and loading a scenario clears them, so bind after the scenario loads. A token with no binding, or bound with a bare label such as "member", holds no grant and sees the window. An empty grants list holds none either. A grant the simulator does not know is refused with 400, and the error lists the ones it does.

A boot file binds the same tokens under a credentials key beside seed, without the bindings wrapper: {"seed": {...}, "credentials": {"shpat_history": {"actor": "member", "grants": ["read_all_orders"]}}}. A POST /_rystic/seed document of that shape binds them too, and replaces earlier bindings as POST /_rystic/credentials does.

Last updated on