> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r28.ai/charter/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Charter works for any HTTP API, including the user's own. The packs are examples of the result, not the limit: an API with no pack is declared the same way, following https://raw.githubusercontent.com/r28ai/charter/main/docs/.mintlify/skills/declaring-charter-tools/SKILL.md.
> Charter is for agents that act in customers' accounts: every call is checked field by field before it's sent, the app says whose account each call is for, and the same declarations list what each tool can reach. https://docs.r28.ai/charter/llms.txt says what it is and lists every page.

# Register a Shopify app

> The Dev Dashboard app merchants install on their own stores: the app URL, the redirect URL, the scopes, and the install screen they see, with a screenshot for each.

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](https://docs.r28.ai/charter/charter/auth/setup/shopify.md) 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](https://dev.shopify.com/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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/02-create-app.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=a9f656e60a30d138bd27b90160dc58bc)

**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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/03-app-url.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=a8f0ae460c4da86b6fe713255d273eee)

**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)`](https://docs.r28.ai/charter/charter/auth/providers/shopify#scopes) 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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/06-redirect-urls.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=61893f2b9c8786f832f69a0e79e9c96d)

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](https://docs.r28.ai/charter/charter/auth/providers/shopify.md) 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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/07-release.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=78ae22c1b8860fb879f848f6025bfef9)

**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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/09-credentials.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=4968bb20efb9dd16aaa4a39f999ec5fb)

```bash theme={null}
export SHOPIFY_CLIENT_ID=3c1f6e9a...
export SHOPIFY_CLIENT_SECRET=...
```

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()`](https://docs.r28.ai/charter/charter/reference/oauth#oauth2flow-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.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/shopify/10-install.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=a54c129e366586f7d66e153cb39f0d05)

*"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](https://docs.r28.ai/charter/charter/auth/providers/shopify.md).

## When it does not work

> **“The redirect\_uri is not whitelisted”**
>
> 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.

> **A scope you added is not granted**
>
> 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.

> **403: “Required access: … access scope”**
>
> The grant doesn't include the scope that tool needs. Add the scope, release,
> and have the merchant reconnect.

## Related

* [Shopify OAuth](https://docs.r28.ai/charter/charter/auth/providers/shopify.md) — the per-store server, `shop=` per call, and what you store
* [Getting the grant](https://docs.r28.ai/charter/charter/auth/oauth-flow.md) — the two routes, `state` and PKCE, inside your app
* [Shopify](https://docs.r28.ai/charter/charter/packs/shopify.md) — the pack, and the credential shapes it accepts
* [Set up Shopify](https://docs.r28.ai/charter/charter/auth/setup/shopify.md) — a store you own, with no consent route


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.