Manage your app credentials
Your app's client credentials consist of a client ID and a client secret. The client ID identifies your app to Shopify, and the client secret authorizes it to make OAuth calls. Some tools and documentation call these the API key and API secret: SHOPIFY_API_KEY holds your client ID, and SHOPIFY_API_SECRET holds your client secret.
Your app can also add public keys. It signs client assertions with the matching private keys to get application-only access tokens for stores that have installed it, without sending its client secret.
Reference this page when you need to locate your credentials for environment configuration, apply security best practices, rotate your client secret after launch, or rotate and revoke public keys.
A service account token is a separate credential for authenticating Shopify CLI in CI/CD, not for API requests. To create, use, or rotate one, refer to Manage service accounts.
A service account token is a separate credential for authenticating Shopify CLI in CI/CD, not for API requests. To create, use, or rotate one, refer to Manage service accounts.
Anchor to Find your credentialsFind your credentials
Your credentials are available in the Dev Dashboard:
- Open the Dev Dashboard.
- Click Apps and select your app.
- Click Settings.
- View or copy your client ID and secret under Credentials.

Your client ID is permanent and doesn't change. Your client secret must be kept secure. Treat it like a password. Shopify also signs your app's webhooks with it, so you use it to verify that webhook requests are genuinely from Shopify.
Your app uses these credentials to get an access token. If your app acts only on stores in your own organization, you can exchange them for a token directly. See Authenticate an app for stores in your organization.
Anchor to Secure your credentialsSecure your credentials
- Never commit credentials to version control. Use environment variables or a secrets manager.
- Your client ID is safe to include in frontend code. Your client secret must never be exposed to the browser or included in client-side code.
- Rotate your client secret regularly, or immediately if you suspect it's been compromised.
- Restrict where your access tokens can be used with an IP allowlist.
- Keep the private keys for your public keys only on your servers, in a secret manager or key management service (KMS). Never upload or paste a private key anywhere, including the Dev Dashboard.
Anchor to Rotate your client secretRotate your client secret
Rotate your client secret if it's been exposed, if an employee with access has left your organization, and as part of regular security hygiene. You can rotate your secret without causing downtime for your app if you follow these steps in order.
The steps below avoid downtime, which is the right approach for routine rotation. If a secret is actively being misused, prioritize locking out the attacker over avoiding downtime: revoke the exposed secret immediately, before you generate and roll out a new one. Merchants lose access until the new secret is in place, but the leaked secret can't be used in the meantime.
Don't revoke your old secret until you've updated all stored access tokens to use the new one. Revoking early can cause merchants to lose access to your app.
Don't revoke your old secret until you've updated all stored access tokens to use the new one. Revoking early can cause merchants to lose access to your app.
Anchor to Step 1: Generate a new secretStep 1: Generate a new secret
- Open the Dev Dashboard.
- Click Apps and select your app.
- Click Settings.
- In the Credentials section, click Rotate next to your client secret.
- Click Generate new secret to confirm.
The new secret appears with a New label. Your old secret stays active until you revoke it.
Anchor to Step 2: Update webhook validationStep 2: Update webhook validation
Shopify signs webhooks with your app's oldest unrevoked client secret, so during rotation your webhooks keep being signed with the old secret until you revoke it. If your app verifies webhook signatures, update your verification logic to accept signatures from both the old and new secret during the transition. Don't switch to validating with only the new secret until after you revoke the old one in the final step, or you'll reject genuine webhooks.
Anchor to Step 3: Update your app to use the new secretStep 3: Update your app to use the new secret
Update your app's environment configuration to use the new client secret for all OAuth calls: token exchange, the authorization code grant, the client credentials grant with a client secret, and refresh token requests. Don't revoke the old secret yet.
Anchor to Step 4: Replace access tokens minted under the old secretStep 4: Replace access tokens minted under the old secret
An access token that your app got with its client secret stays pinned to the secret that minted it, so every such token your app has stored is still tied to the old one. Each of those tokens needs to be replaced with a token minted under the new secret before you revoke the old secret.
Application-only access tokens, which your app gets with a client assertion, aren't tied to a client secret. Rotating or revoking the secret doesn't affect them, so you don't need to replace them. If the private key might also be exposed, for example because you store it alongside your client secret, then also respond to a compromised private key.
How a token gets replaced depends on how your app obtained it:
- Token exchange: The next exchange for a store returns a token pinned to the new secret, so coverage follows merchants opening your app.
- Client credentials grant: Request a new token using the new secret. Tokens from this grant last 24 hours, so they turn over on their own.
- Expiring offline tokens: Each store's token is pinned to the new secret when it refreshes. Let the refresh cycle run, or send a
grant_type=refresh_tokenrequest for each store's stored refresh token to move every store at once.
If a refresh request times out, hits a network error, or returns a transient 5xx, retry promptly with the same refresh_token. The repeated request returns the same rotated credentials rather than issuing another set, so a partial failure during a bulk pass doesn't cost you a store's token. Don't rely on a fixed retry-window duration. See Refresh an expiring offline token for the full error behavior.
If a refresh request times out, hits a network error, or returns a transient 5xx, retry promptly with the same refresh_token. The repeated request returns the same rotated credentials rather than issuing another set, so a partial failure during a bulk pass doesn't cost you a store's token. Don't rely on a fixed retry-window duration. See Refresh an expiring offline token for the full error behavior.
Anchor to Re-pin a non-expiring offline tokenRe-pin a non-expiring offline token
A non-expiring offline token has no refresh token of its own, so none of the paths above reaches it. To move one to the new secret, use the rotation refresh token that the Dev Dashboard issues while two secrets are active.
Non-expiring offline tokens remain available to custom apps. Public apps must use expiring offline tokens for GraphQL Admin API requests, so if yours is public and still holds non-expiring tokens, re-pin them to finish rotation, then migrate to expiring offline access tokens.
- In the Credentials section, find Refresh token and click Generate refresh token. This section only appears while your app has more than one active secret, so you won't see it before Step 1 or after Step 5.
- For each store, send the following request. Include
access_tokenso that Shopify re-pins the existing token rather than running an OAuth refresh. Nogrant_typeis needed.
POST https://{shop}.myshopify.com/admin/oauth/access_token
The response contains a new access token, and the old one is destroyed, so store the response before moving to the next store. The token stays non-expiring. Repeated calls with the same access token within 10 seconds return the same result, so a retry after a network timeout is safe.
A rotation refresh token lasts one hour. If your app is installed on more stores than you can work through in that time, generate another one and continue where you left off.
Admin-created custom apps have no Dev Dashboard entry and no way to rotate a client secret, so this flow doesn't apply to them. To change their credentials, the merchant uninstalls and reinstalls the app.
Admin-created custom apps have no Dev Dashboard entry and no way to rotate a client secret, so this flow doesn't apply to them. To change their credentials, the merchant uninstalls and reinstalls the app.
Anchor to Step 5: Revoke the old secretStep 5: Revoke the old secret
Once every stored access token has been replaced with one minted under the new secret, revoke the old secret in the Dev Dashboard. This finalizes rotation and destroys the old secret row. Any access tokens still pinned to the old secret stop working.
After revoking, update your webhook validation to accept only signatures from the new secret.
Anchor to Rotation and token refreshRotation and token refresh
Two different credentials are called a refresh token in this flow, and they aren't interchangeable:
- A rotation refresh token comes from the Dev Dashboard, covers your whole app, and lasts one hour. You send it with an existing
access_tokento re-pin that token to your new secret, as in Step 4. - A store's refresh token is returned alongside an expiring offline access token, and your app stores one per store. You send it with
grant_type=refresh_tokento renew that store's access token before it expires. See Refresh an expiring offline token.
Rotating a secret invalidates every access token tied to the old secret, so it requires moving all of your stores. Refreshing a token renews one store's access token and leaves your credentials unchanged.
Anchor to Manage public keysManage public keys
Your app uses public keys to get application-only access tokens. Your app keeps each private key, and signs client assertions with it. Shopify stores only the public key, as a JSON Web Key (JWK), and uses it to verify those assertions. To create a key pair and add your first key, follow the application-only access tutorial.
Public keys are app settings, not part of your app's versions or shopify.app.toml, so adding or revoking a key doesn't require you to release a version. Your app can have up to two active keys. You can find them in the Dev Dashboard, under Settings > Credentials > Public keys, which lists each key's Key ID, Thumbprint, and Added date.
An application-only access token depends on the public key that Shopify verified when it issued the token, not on your client secret. Shopify starts rejecting assertions signed with a public key, and the tokens issued with it, shortly after you revoke the key. Revoking a public key doesn't affect tokens issued with your app's other public key, or tokens that your app got with its client secret. Revoking a client secret doesn't affect application-only access tokens.
Anchor to Rotate a public keyRotate a public key
Rotate a public key regularly, and whenever someone who had access to the private key leaves your organization. Because your app can have two active keys, you can rotate without downtime if you follow these steps in order:
- Generate a new key pair, export its public key as a JWK with a new key ID, and add it in the Dev Dashboard.
- Update your servers to sign assertions with the new private key and key ID, and confirm that token requests succeed.
- Wait until the tokens that were issued with the old key expire. Alternatively, make sure that your app requests a new token when the GraphQL Admin API returns
401. - Revoke the old key.
Anchor to Revoke a public keyRevoke a public key
- Open your app in the Dev Dashboard.
- Go to Settings.
- In the Public keys list, click Revoke for the key.
- Click Revoke key to confirm.
If you revoke your app's only key, then your app can't request application-only access tokens until you add another key. After you revoke a key, you can reuse its key ID for a new key, but a new, versioned key ID is easier to track.
Anchor to Respond to a compromised private keyRespond to a compromised private key
If a private key might be exposed, for example because you pasted it somewhere or found it in a log or a repository, then don't wait for a planned rotation:
- Generate a new key pair with a new key ID. If both key slots are occupied, revoke the compromised key first.
- Add the new public key.
- Update your servers to sign assertions with the new key.
- Revoke the compromised key if you haven't already.
- Delete every copy of the compromised private key.
Until you revoke the key, anyone who has the private key can get tokens for every store that has installed your app, and the token lifetime doesn't limit that. Shopify starts rejecting the key and its tokens shortly after you revoke it, so revoke a compromised key as soon as you can.
Anchor to Keep the private key in a KMSKeep the private key in a KMS
You can keep the private key in a key management service (KMS) that doesn't export it. Build the same JWT header and claims that the tutorial's example builds, and sign them with the KMS's RS256 operation (RSASSA-PKCS1-v1_5 with SHA-256) instead of loading a PEM file. Give access to the signing operation only to the service that requests tokens.
Anchor to Next stepsNext steps
- Get application-only access tokens for stores that have installed your app.
- Learn how your app exchanges its credentials for the access token it sends on API requests.
- Use your client secret to verify webhook requests genuinely came from Shopify.
- Create and rotate service account tokens, the separate credential that authenticates Shopify CLI in CI/CD.