Problem:
Our Shopify OAuth handler calls POST https://{shop}.myshopify.com/admin/oauth/access_token (auth-code-grant exchange) without the expiring=1 parameter. Shopify defaults expiring to 0 (non-expiring). As of April 1, 2026, Shopify rejects non-expiring tokens for all public apps created on or after that date with a 403: "Non-expiring access tokens are no longer accepted for the Admin API. Start using expiring offline tokens."
Custom apps (single-store) are exempt, so existing customers are unaffected. But any customer with a public Shopify app created post-April-1 (like empla.io) gets 403 on every Admin API call.
Customer impact:
- Customer: empla.io (du@empla.io), Project:
pr_5bIFbQiiLsoh - Failing connected accounts:
ca_gYhcU6FPcuxW,ca_udW4_DdBvI3z,ca_wqxxJMj-mjag,ca_CqfkWLiP6LhX - Support ticket: T-10568
Root cause (confirmed):
mercury/apps/shopify/config.ts— OAuth2 scheme hasauthorization_params: { access_mode: 'per-user' }which forces online tokens (~24h expiry, no refresh token)hermes/apps/apollo/src/lib/connected_accounts/oAuth2.ts:537-579— token exchange body does NOT includeexpiring=1- No
token_paramsorrefresh_paramsdefined in Shopify config - Apollo's refresh handler at
refreshAccessToken.ts:794-810skips Shopify because no refresh_token exists
Fix needed:
- Add
expiring=1to the token exchange request body (viatoken_paramsin Mercury config or Apollo code) - Switch from
per-user(online) to offline access mode to get refresh tokens from Shopify - Implement refresh token handling for Shopify's + + fields