Since there are many questions about the token expiry requirement spread across many threads I thought it would be useful to bring many of them together here in one consolidated FAQ. As more questions are asked I’ll update this post to include more answers.
Deadlines and scope
What happens on each date?
| Date | Consequence for non-compliant public apps |
|---|---|
| January 1, 2027 | App Store delisting and blocked new installs. Existing installations can continue affected API requests. |
| January 15, 2027 | Merchant warnings in Shopify admin. |
| April 1, 2027 | Affected Admin API requests using non-expiring offline tokens return HTTP 403. |
January 1 remains the migration deadline. April 1 is the API rejection milestone. The earlier requirement for public apps created on or after April 1, 2026 remains in effect.
Which apps and APIs are affected?
The requirement covers public apps using legacy non-expiring offline tokens with GraphQL or REST Admin APIs. Custom apps, merchant-created apps, and online access tokens are outside this migration mandate. Payments Apps API traffic is not subject to the requirement.
How does Shopify detect non-expiring token usage, and why can the warning remain after migration?
Shopify checks the token presented on an Admin API request. Detection is based on using the token, rather than simply holding an unused token. A rejected request can also count as detected usage.
Those request events are aggregated by app for Dev Dashboard. The warning considers usage in the preceding 14 days, so historical usage can remain visible after you migrate.
If you believe your app has fully migrated but are still seeing alerts, check old workers, queued jobs, and infrequent tasks that may still send a legacy token. Acquiring an expiring offline access token through any method does not itself clear earlier usage or show that every request has switched.
I received an email about non-expiring tokens but I don’t see any alerts in the Dev Dashboard
Shopify periodically sends emails to partners that have apps with active non-expiring offline access tokens. If these access tokens have not been used in the last 14 days there will not be an alert in the Dev Dashboard. However, the tokens represent a latent risk to the app because they could be used to make API requests, trigger an alert, and cause the app to be non-compliant with the expiring token policy.
Choosing a token flow
Do I have to use refresh tokens, or can I use application-only access permanently?
Application-only access is a supported long-term acquisition method using the client credentials grant with a signed client assertion. It returns a short-lived offline access token, without a refresh token. Token exchange and the authorization code grant can instead return an expiring offline access token paired with a refresh token. Choose the acquisition and renewal flow that fits the workload.
Which tokens should I use for background jobs versus interactive staff actions?
Use offline access tokens for work performed on the app’s authority, regardless of acquisition method. Use online tokens when a request must respect the signed-in staff member’s permissions. An offline token does not become staff-scoped because the request originated in your UI. See access token types for more details about the different tokens apps can use.
How long do tokens last?
The time to live (TTL) of tokens varies based on the token type and token acquisition method.
| Token | Acquisition method / context | TTL | Renewal |
|---|---|---|---|
| Offline access token | Token exchange or authorization code grant, requesting expiry | 1 hour | Use the refresh token |
| Offline access token | Application-only access; for public apps | Up to 1 hour | Repeat acquisition; no refresh token |
| Offline access token | Client credentials grant with a client secret, or application-only access without public distribution; app and store must be in the same organization | 24 hours | Repeat acquisition; no refresh token |
| Refresh token | Returned with an expiring offline access token | 90 days when issued; can retire earlier | Successful refresh returns a new pair |
| Online access token | User-associated token exchange or authorization code grant | 24 hours or user logout, whichever comes first | Acquire again with a user session |
| Delegate access token | Derived from a parent offline access token | Parent’s remaining lifetime, or a shorter requested TTL | Acquire another delegate |
| Legacy non-expiring offline access token | Earlier offline acquisition without expiry | No time-based expiry | Subject to revocation and the enforcement schedule above |
Use expires_in for the access token’s TTL, and refresh_token_expires_in for the refresh token’s TTL when one is returned.
Does using application-only access invalidate my existing tokens?
No. Application-only access does not retire legacy offline tokens or offline access-token/refresh-token pairs. Token exchange, authorization code acquisition, and refresh also do not retire offline access tokens acquired through application-only access. Multiple offline access tokens acquired this way can remain valid together. Continued affected legacy-token use still leaves the app incompletely migrated.
When does direct migration invalidate the old non-expiring token?
The successful direct exchange invalidates the legacy token immediately. Do not keep using it until the first refresh. Its narrowly supported reuse for migration-response recovery does not restore it as an API credential.
Can separate workers, regions, or customer connections have independent refresh-token families?
The offline flow that returns an access-token/refresh-token pair does not provide independent refreshable families per connection for one app/store. Coordinate acquisition and refresh through shared credential ownership. Application-only access supports overlapping short-lived offline access tokens and may fit independent workers, but those tokens still carry the installation’s app authority; they do not create separate merchant grants or staff identities.
Migrating existing installations
Do merchants need to reopen or reinstall the app to migrate?
No, you do not need to wait for merchants to open your app to migrate old non-expiring tokens. Follow the instructions for migrating without a user session.
Does turning on the SDK/template option migrate every stored token?
Changing acquisition configuration does not convert credentials already stored in your database. Update new token acquisitions to request expiring tokens, then migrate existing installations and update background workers. For direct OAuth/token-exchange implementations, request expiring=1; follow your framework’s configuration instructions.
Can I migrate in batches?
Yes, it’s safe to design your application rollout around individual stored installations. Deploy support for both credential states first, exchange a small cohort, persist the returned pair and expiry information together, and verify real API and background-job behavior before expanding. This is a recommended rollout approach, rather than a promise that every SDK provides batch orchestration. Ensure that you account for concurrent workers and apps that acquire tokens whenever they are opened by the merchant in admin.
What if the migration response is lost or my database write fails?
Retry the same direct migration request with the original legacy token and a valid client ID and client secret within seven days. An eligible retry returns the original pair; recovery ends sooner if that pair is refreshed or superseded by later acquisition.
Refresh, dormant stores, and recovery
Must I refresh every hour for every store, even when there is no work?
No. An unused expiring access token will expire based on its TTL. Refresh when API work resumes while its refresh token is valid. If you rely on refresh tokens, schedule maintenance before their expiry for dormant installations; alternatively implement application-only access where eligible. Receiving a webhook does not itself renew credentials.
What happens to tokens while a store is frozen, including seasonal stores frozen for months?
Apps can refresh expiring offline tokens while a store is frozen. Keep refreshing before the current refresh token expires; freezing does not give you an indefinite credential lifetime. Successful refresh issues a new refresh token with a new 90-day lifetime, without requiring the merchant to reopen the app. You do not need to refresh every hour when there is no API work.
Admin API requests to a frozen store can return HTTP 402 Payment Required. That is a store-state restriction; acquiring or refreshing a token does not unfreeze the store. Pause affected data jobs while retaining credential maintenance. For an unpaid-balance freeze, the merchant must resolve the outstanding payment in admin. See response-code reference for details of this error.
What retires the previous refresh token?
For the normal expiring offline refresh flow, the previous refresh token remains usable until the earliest of:
- Use of its replacement refresh token.
- The 30-day limit measured from its first refresh use.
- Its original expiry (90 days from issuance).
New offline acquisition through token exchange or the authorization code grant immediately retires all other refresh tokens for that app and store. Using the replacement access token is not the acknowledgement that retires the preceding refresh token. See our dev docs for more details on refresh, rotation, and revocation.
What should I do if a refresh response is lost?
Retry promptly with the last stored refresh token while it remains eligible. Persist the returned access token, refresh token, and expiry dates atomically, then use the newest refresh token.
Can I restore an old database backup and use any refresh token saved there?
You can use a refresh token from a backup if it is still valid. If it has expired or been retired by later refreshes or token acquisition, you can regain access through application-only access when the app remains installed and signing-key authentication is configured. Otherwise, the merchant may need to reopen the app and complete authorization. The 30-day refresh recovery period helps with lost refresh responses; it does not restore tokens that have already been retired.
Application-only access and signing keys
What do I need to set up application-only access?
Follow our setup guide for complete details. Generate a signing key pair, register the public JWK in Dev Dashboard, and implement signed client assertions and token acquisition in your backend. Retain the private key securely. Follow the tutorial for supported algorithms, claims, and request format.
Can I request narrower scopes with application-only access?
No. This flow uses the installation’s granted scopes; the request’s scope parameter is ignored. It does not grant scopes the merchant declined or revoked. Use the returned scope information when checking what access is available.
How should I renew offline access tokens acquired through application-only access?
Cache by store, use the response expiry, and acquire again before expiry when needed. Add jitter and transient-error backoff. There is no refresh token for this flow; acquiring another offline access token through the same method does not revoke the still-valid one.
How do key rotation and revocation affect issued tokens?
Adding a replacement public key lets you move signing to the new key before revoking the old one. Revocation causes assertions and offline access tokens tied to that key to be rejected shortly afterward. Client-secret rotation does not revoke offline access tokens acquired through application-only access; public-key revocation does not revoke tokens issued with the other key or the client secret.
Short access-token lifetime limits a leaked access token. A stolen private key can keep obtaining tokens until its public key is revoked.
Is the Dashboard’s “rotation refresh token” a backup for a store’s OAuth refresh token?
No. It belongs to the client-secret rotation workflow. It is not a recovery credential for a store’s expired or retired refresh token.