Skip to main content
A merchant connects a store, not an account, so the store is part of every connection: the consent page, the token endpoint and the API host all live on it. You register one app in Shopify’s Dev Dashboard, each merchant installs it, and you keep one token per store. Only connecting a store you own? Set up Shopify is shorter, because no consent route is involved. Every Shopify link below opens in a new tab, so this page stays where you left it.
1

Create the app

In the Dev Dashboard, click Create app, then name it under Start from Dev Dashboard and click Create. Merchants see this name on the install screen.
The Create an app page, with the app name Example App under Start from Dev Dashboard and a Create button.
2

Set the app URL

Shopify opens the app’s first version. Under URLs, set App URL to your product’s address. This is where a merchant lands after installing, and when the app opens from their admin.
The Create a version page: app name Example App, and under URLs an App URL of https://app.example.com with Embed app in Shopify admin ticked.
Embed app in Shopify admin loads your app inside the admin in an iframe. Untick it unless your app is built for that.
3

Choose the scopes and add your redirect URL

Further down, under Access, set the Scopes the tools need, either with Select scopes or typed in and separated by commas. scopes_for(shopify.TOOLS) lists them, or scopes_for over just the tools you hand out. Below them, add your callback route under Redirect URLs.
The version's Access section: a comma-separated list of scopes, an empty Optional scopes field, Use legacy install flow unticked, and https://app.example.com/oauth/shopify/callback under Redirect URLs.
The scopes here are what Shopify grants on install, whatever the link asks for. In a live install the link asked for read_products, and the install screen listed every scope of the released version. The flow still sends the list in its link, separated by commas, and the server constant on Shopify’s provider page already declares that separator.For development, a plain http://localhost callback works: Shopify accepted http://localhost:8765/callback on a store installing the app for the first time.Leave Use legacy install flow unticked.
4

Release the version

Click Release at the top of the page, then Release again in the dialog. Nothing you set reaches a merchant until a version with it is released.
The Release this new version dialog, with an optional version name and message and a Release button.
5

Copy the client ID and secret

Open the app’s Settings and copy the Client ID and Secret from Credentials.
The app's Settings page: a Credentials panel with the client ID and a masked secret with reveal, copy and Rotate buttons.
The secret also signs every request Shopify sends to your callback, and the callback must check that signature. See the next step.
6

Connect a store

Your “Connect Shopify” button asks for the merchant’s store, then opens the link that flow.authorize() builds on that store. The merchant reviews what the app needs and clicks Install.
The merchant's Install app screen: a notice that the app hasn't been reviewed, the app Example App on My Store, the data it needs access to, and an Install button.
“This app hasn’t been reviewed” appears for any app that isn’t listed on the Shopify App Store. Listing it means going through Shopify’s review.At the callback, check Shopify’s hmac parameter before anything else. It’s an HMAC-SHA256 of the other query parameters, keyed with your client secret. Charter doesn’t check it for you, because it’s Shopify’s own convention rather than part of OAuth. Then exchange the code, with exchange_params={"expiring": "1"} on the server so Shopify issues an expiring offline token and a refresh token. Public apps must use that token for the GraphQL Admin API by January 1, 2027. Store the shop and the grant against the user. The full shape, including the per-call store and the refresh, is in the Shopify provider page.

When it does not work

The flow sent a URL that isn’t under Redirect URLs in the active version. Add it, then release a new version, because an edit that isn’t released doesn’t count.On a store where the app is already installed, that may not be enough. In a live test, redirect URLs added in later released versions were all refused on such a store, over http and https alike, and with the app URL moved onto the same host. The URL the app had when it was installed was accepted. After uninstalling the app and installing it again through the flow, the new URL was accepted. So test a new URL on a store that hasn’t installed the app, or reinstall it.
Scope changes reach a store only through a released version, and a store that installed earlier keeps its old grant until the merchant approves the new scopes. Release, then send the merchant through the link again.
The grant doesn’t include the scope that tool needs. Add the scope, release, and have the merchant reconnect.
  • Shopify OAuth — the per-store server, shop= per call, and what you store
  • Getting the grant — the two routes, state and PKCE, inside your app
  • Shopify — the pack, and the credential shapes it accepts
  • Set up Shopify — a store you own, with no consent route