I am trying to set up a custom app using the Dev Dashboard. I created the app, which gives me an API key and a secret. However, to initialize the shopifyApi object in JS, I need to provide an adminApiAccessToken. I am trying to follow the Shopify documentation to obtain this. I have installed the custom app on my testing store. I go to Settings > Apps and Sales Channels > Develop Apps. I do NOT see my app listed, in fact I don’t see any list of apps at all. Can someone please explain what I am missing here?
Hi @ayan4m1! I can see where the confusion is coming from. Apps created in the Dev Dashboard don’t show up in Settings > Apps and Sales Channels > Develop Apps in your store admin - that section is only for legacy custom apps, which are being deprecated January 1, 2026.
When you create an app in the Dev Dashboard, you don’t get an Admin API access token displayed anywhere. Instead, you need to use the client credentials grant, token exchange or authorization code grant flows instead.
This was covered in detail in this thread where Kellan walked through the whole transition from legacy custom apps to Dev Dashboard apps. Worth reading through if you want the full context on why things changed.
The Client ID from the Dev Dashboard is equivalent to the old “API key”, and the Client Secret is the old “API secret key” if you’re mapping between the two systems. Let me know if there’s anything I can clarify for you!
I tried using the client credentials grant with this code
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET_KEY,
scopes: ['read_products'],
hostName: process.env.SHOPIFY_SHOP_NAME,
apiVersion: ApiVersion.October25
});
const client = new shopify.clients.Graphql({
session: (await shopify.auth.clientCredentials({
shop: shopify.utils.sanitizeShop(process.env.SHOPIFY_SHOP_NAME)
})).session
});
If I use my testing shop name in SHOPIFY_SHOP_NAME, I get
[shopify-api/INFO] Creating new session | {shop: bcf-testing.myshopify.com, isOnline: false}
MissingRequiredArgument [Error]: Missing access token when creating GraphQL client
at new GraphqlClient (file:///C:/code/bcf-review-scraper/node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client.mjs:16:
19)
at new GraphqlClient (file:///C:/code/bcf-review-scraper/node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client.mjs:83:
5)
at file:///C:/code/bcf-review-scraper/import.js:16:18
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
If I try to use the live store (the app has 2 installs, the dev and live store), I get this error
statusText: 'Bad Request',
body: {
error: 'shop_not_permitted',
error_description: 'Client credentials cannot be performed on this shop.'
},
Also, sorry, but that reply you linked to does not really provide any context to me - I understand that apps need to be created in the dev dashboard moving forward, but I don’t understand why your documentation is not accurate to how the site is currently operating.
Can you please explain why I am getting the 400 Bad Request for the live store and the missing access token error for the dev store?
Thanks for following up @ayan4m1 I could have been clearer about when to use each of the different authorization types.
Client credentials only works when the app and store are owned by the same organization. So it works for a merchant-owned app authenticating to that merchant’s own store, or a Partner app authenticating to a dev store in the same Partner org. It doesn’t work for Partner apps authenticating to production stores (even if the app is installed there), which is why you’re getting the shop_not_permitted error on your live store.
For the error on your testing store, the isCustomStoreApp: true flag is only for legacy custom apps that were created directly in the Shopify admin (the ones being deprecated Jan 1, 2026). Apps created in the Dev Dashboard don’t use that flag as they don’t get a displayed access token - they need to go through an OAuth flow instead.
The option that likely best fits your use-case is the authorization code grant flow. This gives you a non-expiring offline token that stays valid until the app is uninstalled, whereas client credentials tokens expire every 24 hours and need to be refreshed.
I agree the documentation could be clearer on the different authorization types and when to use them - I’ll be sure to submit some feedback to the docs team on your behalf.
Let me know if I can clarify anything further for you and I’ll be happy to do so!
Sorry, I think I’m misunderstanding. You’re telling me that to use the Shopify JS API, I need to manually implement all of this token request flow stuff - I need to make a manual POST request to your API just to get an access token? This is a huge effort just to authenticate, and it’s code that is going to be duplicated by hundreds/thousands of developers in different weird ways. Why is this not handled by a method under shopify.auth? How is it possible that you have made it this complicated for developers to do the most basic thing with the GraphQL API?
EDIT: Sorry, I’ve read Implement authorization code grant manually a few times now and I am completely lost, I don’t understand why this got so complicated, this is so depressing.
Seriously. Not really sure why offline apps must have a “online” oauth endpoint just to handle the handshake and then never have another interaction. Really missing the mark here Shopify.
@Donal-Shopify Do you have any further info for us or are we really intended to have to set up an entire hosted HTTP server just to get an access token for a single store after Jan 1?
I completely get the frustration - you do need a hosted endpoint to handle the OAuth callback. It’s a one-time setup during installation, but it does require a server.
Authorization code grant gives you a permanent, non-expiring offline token (not a 24-hour token like client credentials). The OAuth flow runs once during app installation, exchanges the authorization code for a permanent token, and then you’re done - that token works forever for your background scripts until the app is uninstalled.
Client credentials won’t work for your case because it only works when the app and store are owned by the same organization. Partner apps can’t use it on merchant stores (even your own merchant store), which is why you got the shop_not_permitted error.
The minimal setup you need:
-
Host a simple callback endpoint (can be serverless like Cloudflare Workers)
-
Merchant clicks the install link from Dev Dashboard
-
Shopify redirects to your callback with an authorization code
-
Your endpoint makes one POST request to exchange the code for a permanent token
-
Store that token somewhere secure
-
Use it for all future API calls - no expiration, no refresh needed
When you exchange the authorization code for the token, don’t include expiring=1 in the POST request. The default (expiring=0) gives you a non-expiring token. The response will just have access_token and scope - no expires_in field because it doesn’t expire. The authorization code grant docs show the full flow.
I know it’s more work than the legacy flow. The endpoint can be minimal - literally just receive the code, POST to get the token, save it, and redirect. After that one-time setup, your backend scripts work exactly like before with a permanent token.
I do appreciate you raising this - you’re not alone in finding this frustrating, and all this feedback is taken onboard by the team
That’s ridiculous. I work as a “shopify developer” helping my friends out with single-store app installs. I won’t be able to do that for them moving forward with all this extra work involved. Shopify should be ashamed at thinking this is a proper solution.
Thanks for your reply. Thank god I was able to make a legacy app before Jan 1 so I can actually get my WORK done.
I’ve gone ahead and removed the inappropriate words from your post. Please be more cautious as to what you post.
Well it has been a VERY busy start of the year with so many broken developers. Many merchants relied upon these people to provide helpful code but this is now broken. Good for us I suppose but bad for Shopify as a whole.