Skip to main content

Delegate API access

When you install an app on a store, Shopify issues an access token with all the scopes the merchant approved. Delegating API access means creating a scoped-down version of that access token (the parent token) for a specific part of your system. Use delegate tokens when your app is split across multiple server-side subsystems, like separate services or background jobs. For example, a job that processes orders doesn't need read access to products.

Don't embed delegate tokens in distributed clients such as mobile or desktop apps, where users can extract the token from the binary, memory, or network traffic and replay it against Shopify APIs with the delegated scopes.

Caution
Treat delegate access tokens as secrets. Don't expose them in client-side code or share them outside the subsystems that need them.

Anchor to Create a delegate access tokenCreate a delegate access token

Use the delegateAccessTokenCreate mutation to request a delegate token, passing in the scopes the subsystem needs. To use the delegate token with the GraphQL Admin API, pass it in the X-Shopify-Access-Token header, the same way you'd use any access token. To use it with the Storefront API, pass it as a private access token in the Shopify-Storefront-Private-Token header.

The following example requests a delegate access token with write_orders permissions. It requests expiresIn in the response to read back the token's actual expiry, inherited from the parent token.

Example request for a delegate access token

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

mutation {
delegateAccessTokenCreate(input: { delegateAccessScope: [
"write_orders" ]}){
delegateAccessToken {
accessToken
expiresIn
}
shop {
id
}
userErrors {
field
message
}
}
}

Response

{
"data": {
"delegateAccessTokenCreate": {
"delegateAccessToken": {
"accessToken": "shppa_12345678910",
"expiresIn": 86400
},
"shop": {
"id": "gid://shopify/Shop/1"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 1000.0,
"currentlyAvailable": 990,
"restoreRate": 50.0
}
}
}
}

FieldRequired?Description
delegateAccessScopeYesThe list of scopes to delegate. You can only delegate scopes your app was already granted.
expiresInNoThe amount of time, in seconds, after which the delegate access token is no longer valid. You can't set this longer than the parent token's remaining lifetime. If omitted, the token expires at the same time as the parent.

To delegate a scope your app doesn't have yet, add it to your app's configuration and have the store approve it. Where you add it depends on how your app is set up:

Two behaviors to plan for:

  • If your app is later re-authorized with fewer scopes, every existing delegate token loses the scopes that are no longer authorized. The tokens keep working for whatever scopes remain.
  • A delegate access token can't create more delegate access tokens. Only your app's own access token can.

FieldDescription
accessTokenThe delegate access token string. Pass this in the X-Shopify-Access-Token header for GraphQL Admin API requests, or Shopify-Storefront-Private-Token for Storefront API requests.
expiresInThe token's actual expiry in seconds, reflecting the parent token's remaining lifetime or the value set on input. Available in the 2026-04 API version and later.

Anchor to Rotate delegate tokensRotate delegate tokens

Delegate tokens inherit the parent token's expiry, and refreshing the parent token doesn't extend delegate tokens that already exist. After each parent token refresh, create new delegate tokens and rotate them into your subsystems.

To schedule rotation precisely, request expiresIn in the mutation's response and store the value it returns. Setting a shorter expiresIn per subsystem limits exposure if one of its tokens is compromised.

Two behaviors are worth planning for:

  • If the app is re-authorized with fewer scopes, every delegate token immediately loses the scopes that are no longer authorized.
  • A delegate access token can't create further delegate access tokens, so each one must come from the parent token.


Was this page helpful?