See all services → Shopify Plus Partner
API Developer

Downgrade App Write Scopes to Read From 2027-01

Admin GraphQL API version 2027-01 introduces appDowngradeAccessScopes, letting apps using Shopify-managed installation swap optional write scopes for their matching read scopes without a full reinstall.

Executive summary

What changed

The mutation operates only on the calling app's own installation, which is a sensible constraint and worth stating plainly. The scopes you pass must be declared in optional_scopes, must be write scopes, and must have a matching read scope. Required scopes cannot be downgraded. Write scopes that are not currently granted are simply ignored.

Validation is all or nothing. If any requested scope fails validation the whole request is rejected and the installation's scopes stay unchanged, which means you will not end up in a half-applied state. The response returns a downgraded field listing the write scopes that were successfully changed, alongside userErrors for anything that failed. A call downgrading write_products to read_products is a single mutation returning the downgraded handles.

Why it matters

This is a developer convenience rather than a merchant feature, and its value is least-privilege hygiene. Enterprise apps accumulate scopes: a write scope requested for a one-time backfill tends to stay granted indefinitely, because removing it risked the read access other parts of the app still depended on. That made the safe choice look expensive, so teams left write access in place.

For a Plus merchant, the relevant question is governance. Each app holding write access to products, orders, or customers is a surface that appears in security reviews and vendor assessments. Being able to narrow scopes without a reinstall makes periodic access review a realistic practice rather than an aspiration, which matters most for custom Shopify apps built years ago and still running. If you request a scope list from your app vendors annually, this is the mechanism that lets them actually act on the result.

Role-specific impact

Use-case example

Real-world scenario

A merchant runs a private app built three years ago to sync a product catalogue from a PIM. It holds write_products because it once performed the initial load and ongoing enrichment. Enrichment has since moved to middleware, so the app only reads product data to build a reconciliation report, yet it still carries write access to roughly 14,000 products. Under the old behaviour, revoking write_products would have removed the read access the report relies on, so the scope stayed. With this mutation the team downgrades to read_products in a single call, the report keeps working, and one app with unnecessary write access to the whole catalogue drops off the next security review.

Implementation checklist

  1. Confirm the app uses Shopify-managed installation, since the mutation does not apply otherwise.
  2. List currently granted scopes and identify write scopes that are no longer exercised by any code path.
  3. Check that each candidate is declared in optional_scopes and has a matching read scope, because required scopes cannot be downgraded.
  4. Upgrade the app to Admin GraphQL API version 2027-01 when it becomes available.
  5. Call appDowngradeAccessScopes with the target write scopes and inspect both downgraded and userErrors in the response.
  6. Exercise the app's read paths afterwards to confirm nothing depended on write access implicitly.
  7. Record the resulting scope set so the next access review starts from a known baseline.

FAQ

Resources

appDowngradeAccessScopes mutation reference

Need guidance? Talk to Makro.