The API

3. Connect them

Proxy, scopes, installing the app, and verifying.

You have an app and a deployed API. Four steps left, and the third is the one almost everyone misses.

1. Point the proxy at the deployment

In shopify-app/shopify.app.toml, using the URL Netlify gave you:

1[app_proxy]
2url = "https://your-site.netlify.app"
3prefix = "apps"
4subpath = "admin-api"

subpath must match what the theme asks for. It defaults to admin-api, so if you use anything else you'll change a theme setting in step 4.

⚠️ prefix and subpath are frozen when the app is installed. Changing them later needs an uninstall and reinstall — a redeploy is not enough. Decide now.

2. Declare the access scopes

Still in the same file. These control what the Admin API will let your operations do:

1[access_scopes]
2scopes = "read_products,read_inventory,read_locations,write_customers"

Don't guess at these — npm run api:build prints the exact line, worked out from the # Scopes: headers in your .graphql files:

Access scopespaste into shopify-app/shopify.app.toml:
[access_scopes]
scopes = "read_inventory,read_locations,read_products,write_customers"

Getting this wrong doesn't fail at setup. It fails later, at the Admin API, with an access-denied error that names neither the missing scope nor the operation that needed it.

3. Deploy the app, then install it

cd shopify-app
npx shopify app deploy

That publishes the configuration — the proxy path, the scopes — to Shopify.

And that is not enough. Deploying tells Shopify what the app is. It does not put it on your store. Until the app is installed, the proxy doesn't exist for that storefront, and your-store.com/apps/admin-api/admin.gql returns a bare 404 that never reaches Netlify.

npx shopify app dev --store your-store.myshopify.com

That prints an install link — open it and accept the scopes. In the Dev Dashboard the same thing lives under your app → Test on development store.

Check where you stand at any time:

npx shopify app info
Dev store Not yet configurednot installed anywhere; 404 is expected
Access scopes read_products,… ← what the app will ask for

4. Point the theme at the proxy

The theme's API base defaults to /apps/admin-api, which matches the default subpath. If those agree, there is nothing to do.

If you chose a different subpath, set it in the theme editor under Theme settings → API.

A mismatch here produces a 404 from Shopify that never reaches your deployment — so the API looks broken when it is only mounted somewhere else.

5. Verify end to end

From your storefront — the browser console on any page will do:

1const { shop } = await admin('GetShop')
2console.log(shop.name)

Your store's name comes back. That request went storefront → Shopify → App Proxy → Netlify → Admin API and back, signed the whole way, with no credential ever in the browser.

When it doesn't work

Work down this list in order — each rules out the one below it.

What you seeWhat it means
404 on /apps/admin-api/…The app isn't installed. shopify app infoDev store Not yet configured
404 after changing subpathThe path was frozen at install. Uninstall and reinstall
500 Server is not configuredSHOPIFY_API_SECRET missing on Netlify, or set but not redeployed
401 Invalid signature from the storefrontThe secret on Netlify isn't the app's real Client secret
401 Invalid signature from curlExpected — a direct request has no signature
Could not obtain an Admin API tokenWrong client id/secret, or app and store in different organizations
Access-denied from the Admin APIA missing scope. Add it, shopify app deploy, reinstall
A new operation isn't foundNot compiled in yet. npm run api:build, then redeploy

The deployment logs carry Shopify's own message for token failures, which is usually more specific than anything the gateway can say without leaking credentials to the browser.

The whole picture

Storefront Shopify Netlify
────────── ─────── ───────
admin('GetShop')
└──▶ /apps/admin-api/admin.gql
verifies the request came
from this shop, signs it
POST /admin.gql ──────────────▶ verify signature
(client secret)
look up "GetShop"
in the allowlist
exchange id+secret
for a 24h token
Shopify Admin API

The browser names an operation. The server owns the query text, the credentials and the destination. That's what makes it safe to expose to a storefront at all — see why it takes a name.