Logo

What Has Changed in the Shopify Offline Token Refresh Mechanism?

avatar jayce 04 Nov 2026

If you have been maintaining the authorization code for a Shopify App recently, this update is worth a closer look.

On December 10, 2025, Shopify stated in the developer changelog: offline access tokens now support expiration and refresh. By March 20, 2026, another changelog added a stronger note: any new public app created and calling the Admin API after April 1, 2026, must use an expiring offline access token.

This change does not immediately disrupt all existing applications. Current public apps, custom apps, and apps created by merchants in the backend are not subject to this April 1 rule. However, if you are developing a new application or launching a new public app this year, it is best to incorporate this refresh logic from the first version.

The Assumption of “Install and Use Forever” is Gone

Many backend tasks of Shopify Apps rely on offline tokens. For instance, synchronizing orders, processing webhooks, periodically pulling products, and generating reports for the store. The app needs to call the Admin API even when the merchant is offline, so the offline token has always been the default choice.

The common handling in old code is straightforward: the merchant obtains the token upon installation and stores it in the database; then, with each request to the Admin API, it is included in the X-Shopify-Access-Token. As long as the merchant does not uninstall the app, the team usually assumes that this token remains valid.

The new expiring offline token dismantles this assumption. The official documentation now clearly states: the validity period of an expiring offline access token is 1 hour, with expires_in in the response set to 3600. It also returns a refresh_token, allowing the application to obtain a new access token without disturbing the merchant.

For example, an independent store selling pet supplies with around 200 SKUs runs inventory synchronization every 15 minutes. Previously, the synchronization task only needed to read the offline token from the store table. Now, it must check at least two additional fields: when the access token expires and whether the refresh token is still valid. Otherwise, if the synchronization task encounters a 401 error at midnight, the entire inventory for the night could come to a halt.

Refreshing is Not Re-running the Installation Process

A key aspect of this Shopify change is that the refresh occurs at the token endpoint, without requiring the merchant to reauthorize.

The official documentation provides the OAuth token endpoint as follows:

POST https://{shop}.myshopify.com/admin/oauth/access_token

If you are using token exchange, you can include requested_token_type=urn:shopify:params:oauth:token-type:offline-access-token when obtaining the offline token and set expiring to 1. If you are using the authorization code grant, you can request an expiring offline token by including expiring=1 when exchanging the code.

In the response, you need to save not only the access_token but also the following:

• expires_in: the remaining seconds for the current access token, which is currently 3600 seconds according to the official documentation.

• refresh_token: to be used for the next server refresh.

• refresh_token_expires_in: the initial validity period of the refresh token, which is stated as 7776000 seconds, or 90 days.

Do not hardcode 3600. The official documentation also reminds that the application should read expires_in from the response and not assume the validity period remains unchanged.

The Most Likely Pitfall is Concurrent Refreshing

The refresh logic may seem ordinary, but the real trouble lies in the rotation.

Shopify’s documentation is detailed: each refresh will return a new access token and a new refresh token. The application must store both, using the new refresh token for the next refresh.

The old refresh token does not immediately become invalid the moment Shopify issues a new token. It will become invalid at the earliest of several conditions: the application has used the new refresh token; the application has obtained a new token through token exchange or authorization code grant; the old refresh token has been used for refreshing for the first time for 30 days; or its original 90-day expiration has been reached.

This set of rules has a direct impact on implementation. Suppose your webhook worker and scheduled task both discover that the token is about to expire, and both processes attempt to refresh simultaneously. They both write the results back to the database, and the later write will overwrite the earlier one. The next request may depend on which refresh token is retrieved due to a race condition.

A more stable approach is to lock at the store level or perform conditional updates in the database: only allow writing a new token if the refresh token in the current record is still the one you just read. If the refresh fails, read the record again before kicking the merchant back to OAuth.

401 is Not the Only Trigger Point

Refreshing only after receiving a 401 can recover some requests, but it will result in an additional failure for backend tasks. This is acceptable for tasks like webhooks that have retries, but for manually triggered exports or bulk updates, the experience may be quite rough.

The official recommendation is to refresh a few minutes in advance and to supplement the refresh branch again upon receiving a 401. In implementation, you can save expires_at for each store and check before requests whether it is nearing expiration. For example, if it is within 5 minutes of expiration, refresh before making the Admin API request.

Refresh failures should also trigger separate alerts. The reasons could be an expired refresh token, a rotated client secret, the merchant uninstalling the app, or your storage not correctly saving the new refresh token. They may all manifest as a 401, but the handling methods differ.

Old Applications Can Take a Breather, But Don’t Continue Accumulating Technical Debt

The requirement for April 1, 2026, specifically targets newly created public apps. Shopify has also stated that public apps created before April 1 are temporarily unaffected.

However, there is another timeline in the access tokens documentation: public apps must use expiring offline access tokens to request the Admin API by January 1, 2027. Custom apps and merchant-created apps are not included in this scope.

So, while old applications are not required to migrate today, if you are already modifying the authorization layer, it is best to also prepare the table structure and refresh paths. At the very least, remove comments like “offline tokens never expire” and stop categorizing 401 errors as simply due to merchant uninstallation in backend tasks.

I have not yet tried to confirm the internal implementation of the latest library line by line in a real Shopify template project. The official statement is that if you use Shopify app templates and libraries, this part has already been handled. For self-built OAuth, old Rails apps, or projects that have modified the template’s authorization layer, you should still check the token storage and refresh paths yourself.

You Can Start by Changing These Few Areas

There is no need to rewrite the entire authorization system right away. Start by looking at four areas:

• Is there a place in the database to store refresh_token, expires_at, refresh_token_expires_at?

• In the Admin API request wrapper, can you check the expiration time before making requests?

• Will backend tasks and webhook workers concurrently refresh the token for the same store?

• In the 401 logs, can you distinguish between token expiration, insufficient scope, and app uninstallation?

If the application is still using non-expiring offline tokens, the new version can first add expiring=1 during installation or token exchange, and then connect the refresh logic to a unified API client. This way, product synchronization, order synchronization, and reporting tasks do not need to write refresh code separately.

This change is not flashy, but it resembles Shopify moving App authorization towards more standard OAuth behavior. Backend tasks can continue to run, but in the future, they can no longer rely on a permanent token to sustain the entire lifecycle.

Official Sources:

• https://shopify.dev/changelog/offline-access-tokens-now-support-expiry-and-refresh

• https://shopify.dev/changelog/expiring-offline-access-tokens-required-for-public-apps-april-1-2026

• https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens

Book Free 30-Min Call