> This page is for Developer.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bigcommerce.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bigcommerce.com/_mcp/server.

# Authenticating requests to the GraphQL Storefront API

Authenticate GraphQL Storefront API requests using bearer tokens passed with the `Authorization` header. You can authenticate using three different kinds of tokens: [storefront tokens](#storefront-tokens), [private tokens](#private-tokens), or [customer impersonation tokens](#customer-impersonation-tokens).

**`Example request configuration`**

```http title="Example request configuration" showLineNumbers={false}
POST https://your_store.example.com/graphql
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json

{
  // request body
}
```

## Storefront tokens

> **Note**
>
> Storefront tokens used in server-to-server contexts are deprecated. Use [private tokens](#private-tokens) for server-to-server and headless interactions instead. Storefront tokens will continue to work for browser-side requests. The deprecation timeline is as follows:
>
> * **June 30, 2026**: Storefront tokens can no longer be created without `allowed_cors_origins`. Existing tokens without origins will continue to work.
> * **March 31, 2027**: Storefront tokens without `allowed_cors_origins` will no longer be accepted for API requests.

Storefront tokens are most appropriate to use directly from the web browser. If you're creating a token for an application that will make server-to-server or proxied requests to the GraphQL Storefront API, use a [private token](#private-tokens). If you work with customer data, use a [customer impersonation token](#customer-impersonation-tokens). If you only wish to query information from an anonymous shopper's perspective in a browser, use a storefront token.

### Storefront token security

Generally speaking, vanilla storefront tokens are not considered sensitive, and it is safe to expose them in web browsers. Storefront tokens can only expose information and actions that shoppers can access when they browse a storefront.

It is possible to create a long-lived token that does not expire. For greater security, we recommend creating shorter-lived tokens and rotating them periodically.

For security reasons, GraphQL Storefront API tokens are scoped to particular [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) origins, so you must supply the origin or origins on which you intend to use the token. If you have more than two origins, you will need multiple tokens. For server-to-server or headless interactions that do not require CORS origins, use a [private token](#private-tokens) instead.

### Create a storefront token

You can create storefront tokens by API. On Stencil storefronts, you can also [access an autogenerated token](#using-auto-generated-tokens-on-stencil-storefronts) using in-page context.

Use the [Create a storefront token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/create-token) REST endpoint to create storefront bearer tokens. Add the [storefront API tokens creation scope](/developer/docs/overview/api-fundamentals/api-accounts#token-creation-scopes) to the [store-level or app-level API account](/developer/docs/overview/api-fundamentals/api-accounts#api-accounts) you use to generate tokens. You can use a storefront bearer token to create a [customer access token](/developer/docs/storefront/guides/graphql-storefront-api/customer-context#customer-access-tokens).

> **Note**
>
> The channel ID for the default Stencil storefront is `1`. Storefront tokens don't require you to pass in channel IDs if you want to make the request on behalf of all the stores channels IDs. To learn more about channels, see [the channels section of the multi-storefront overview](/developer/docs/admin/multi-storefront#channels). For more about using the GraphQL Storefront API on custom channels, consult the GraphQL Storefront API overview's [FAQ section on alternate channels](/developer/docs/storefront/guides/graphql-storefront-api#i-want-to-run-requests-in-the-context-of-another-channel).

#### Request

**`Example request: Create a storefront API token`**

```http title="Example request: Create a storefront API token" showLineNumbers={false}
POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token
x-auth-token: {{access_token}}
accept: application/json
content-type: application/json

{
  "channel_ids": [1, 2, 3],            // array of integers (must be a valid channel IDs on the store)
  "expires_at": 1602288000,   // when the token will expire, as an integer unix timestamp (in seconds)
  "allowed_cors_origins": [   // array of origins (up to 2 origins per token)
    "https://example.com"
  ]
}
```

#### Response

**`Example response: Create a storefront API token`**

```json title="Example response: Create a storefront API token" showLineNumbers={false}
{
  "data": {
    "token": "...eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9..."
  },
  "meta": {
    // ...
  }
}
```

#### Customer access tokens

A customer access token establishes [customer context](/developer/docs/storefront/guides/graphql-storefront-api/customer-context) for server-to-server requests: it represents an authenticated storefront session and is sent in the `X-Bc-Customer-Access-Token` header alongside a storefront or private token. For how to obtain and use one, see [Customer access tokens](/developer/docs/storefront/guides/graphql-storefront-api/customer-context#customer-access-tokens).

### Using auto-generated tokens on Stencil storefronts

On Stencil storefronts, you can access a token at render time and pass the token to client-side code using the `{{settings.storefront_api.token}}` Handlebars property. This auto-generated token has an expiry period of 24-48 hours and will rotate before expiration.

## Private tokens

Private tokens are designed for server-to-server and headless storefront interactions with the GraphQL Storefront API. Unlike storefront tokens, private tokens do not require CORS origins and should never be exposed in a web browser.

Private tokens support the following access scopes:

* **Unauthenticated**: Query information from the perspective of an anonymous shopper. This is equivalent to an unauthenticated storefront session.
* **Customer**: Access customer-specific data, such as product pricing and availability, customer account details, and more. You must pass the `X-Bc-Customer-Access-Token` header along with the private token.
* **B2B**: Access B2B-specific data, such as company accounts, buyer roles, and shared shopping lists.

### Create a private token

Add the [Storefront API tokens creation scope](/developer/docs/overview/api-fundamentals/api-accounts#token-creation-scopes) to the [store-level or app-level API account](/developer/docs/overview/api-fundamentals/api-accounts#api-accounts) you use to generate tokens.

#### Request

**`Example request: Create a private token`**

```http title="Example request: Create a private token" showLineNumbers={false}
POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token-private
X-Auth-Token: {{access_token}}
Accept: application/json
Content-Type: application/json

{
  "channel_ids": [1, 2, 3],          // array of integers (must be valid channel IDs on the store)
  "expires_at": 1602288000,          // when the token will expire, as an integer unix timestamp (in seconds)
  "scopes": ["Unauthenticated"]      // required; one or more of: "Unauthenticated", "Customer", "B2B"
}
```

#### Response

**`Example response: Create a private token`**

```json title="Example response: Create a private token" showLineNumbers={false}
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
  },
  "meta": {}
}
```

### Private token security

Private tokens *are* sensitive and should **never** be exposed publicly, including in client-side code or web browsers. Treat them with the same care as other application secrets, such as API account access tokens.

If your token is compromised, you can use the [Revoke a token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens#revoke-a-token) endpoint. Only use this in emergencies; do not revoke tokens unnecessarily. Instead, use a shorter expiration and allow them to expire naturally.

## Customer impersonation tokens

> **Note**
>
> Because of the flexible use of the customer impersonation token, BigCommerce recommends using a more secure option like the [customer access token](/developer/docs/storefront/guides/graphql-storefront-api/customer-context#customer-access-tokens). Customer impersonation tokens allow you to access any customer by simply changing the `X-Bc-Customer-Id` value in the header for server-to-server communications. However, the customer access token is tied directly to a specific customer as part of the token payload providing a more secure experience. In addition, customer access tokens have the following added benefits:
>
> * A customer access token is unique to an individual user's account.
> * A customer access token allows users to act on different systems.
> * A customer access token becomes invalid on all devices when you log out on a single device.

You can use customer impersonation tokens to authenticate requests to the GraphQL Storefront API in server-to-server and headless interactions. The API will reject any customer impersonation token-authenticated requests that originate from a web browser and are not proxied.

> **Note**
>
> In the future, we will not support all customer features when using a customer impersonation token. More security-sensitive features will only work with the customer access token. For example, features like exposing a customer's Saved Payment Methods will only work with a customer access token.

Customer impersonation token-authenticated requests will be specific to the customer, including product pricing and availability, customer account details, and more.

It is not necessary to generate a new token for each customer ID. You may use a single token at any given time for your application and specify a distinct customer ID for each set of requests.

Using a customer impersonation token does not automatically sign a customer in. For that, use the [customer login mutation](#signing-customers-in) or the [Customer Login API](/developer/api-reference/rest/admin/authentication-apis/customer-login-api).

### Create a customer impersonation token

Add the [Storefront API customer impersonation tokens creation scope](/developer/docs/overview/api-fundamentals/api-accounts#token-creation-scopes) to the [store-level or app-level API account](/developer/docs/overview/api-fundamentals/api-accounts#api-accounts) you use when you [Create a customer impersonation token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens).

#### Request

**`Example request: Create a customer impersonation token`**

```http title="Example request: Create a customer impersonation token" showLineNumbers={false}
POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token-customer-impersonation
X-Auth-Token: {{access_token}}
Accept: application/json
Content-Type: application/json

{
  "channel_ids": [1, 2, 3], // array of integers (must be a valid channel IDs on the store)
  "expires_at": 1602288000 // when the token will expire, as an integer unix timestamp (in seconds)
}
```

#### Response

**`Example response: Create a customer impersonation token`**

```json title="Example response: Create a customer impersonation token" showLineNumbers={false}
{
  "data":
  {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
  },
  "meta": {}
}
```

### Customer impersonation token security

Unlike storefront API tokens, customer impersonation tokens *are* sensitive and should **never** be exposed publicly. Treat them with the same care as other application secrets, such as API account access tokens.

If your token is compromised, you can use the [Revoke a token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens#revoke-a-token) endpoint. Only use this in emergencies; do not revoke tokens unnecessarily. Instead, use a shorter expiration and allow them to expire naturally.

## Managing and revoking tokens

You can list the tokens your API account has issued and revoke them by ID without needing the token's JWT string. These endpoints work identically for each token family; only the path segment and the required OAuth scope differ:

| Token family           | Path segment                                   | Required scope                                                                                 |
| :--------------------- | :--------------------------------------------- | :--------------------------------------------------------------------------------------------- |
| Storefront             | `/storefront/api-token`                        | `Storefront API Tokens` (`store_storefront_api`)                                               |
| Private                | `/storefront/api-token-private`                | `Storefront API Tokens` (`store_storefront_api`)                                               |
| Customer impersonation | `/storefront/api-token-customer-impersonation` | `Storefront API Customer Impersonation Tokens` (`store_storefront_api_customer_impersonation`) |

Listing and revocation are scoped to the API account that makes the request. You can only see and revoke tokens that the same API account created; tokens issued by a different API account are never returned by the list endpoints, and revoking one by `jti` returns `404 Not Found`.

The list endpoints, [revoke a token by ID](#revoke-a-token-by-id), and [revoke all tokens](#revoke-all-tokens) only cover tokens of the family named in the path segment. A `jti` that belongs to another family returns `404 Not Found`. Customer impersonation tokens require their own scope, so an API account without it receives `403 Forbidden` from every customer impersonation path.

> **Info**
>
> Revocation can take up to approximately 60 seconds to fully propagate because revoked identifiers are held in a short-lived cache. A revoked token may briefly continue to authenticate during this window.

The examples below use the storefront (`/storefront/api-token`) family. For private or customer impersonation tokens, swap the path segment to `/storefront/api-token-private` or `/storefront/api-token-customer-impersonation`.

### List tokens

The [List tokens](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/list-tokens) endpoint returns a paginated list of the tokens your API account has issued. The token's JWT string is never returned; each entry includes a `jti` identifier that you use to revoke the token, as well as `is_revoked` and `is_expired` flags. Revoked and expired tokens are included in the list. You can page and sort the results using the optional `page`, `limit`, `sort` (`issued_at` or `expires_at`), and `direction` (`asc` or `desc`, defaulting to `asc`) query parameters. An unrecognized `sort` field, `direction` value, or query parameter returns `422 Unprocessable Entity`.

#### cURL

**`Example request: List tokens`**

```shell title="Example request: List tokens" showLineNumbers={false}
curl -sS -X GET \
  -H "X-Auth-Token: {{access_token}}" \
  -H "Accept: application/json" \
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token?limit=50&sort=issued_at&direction=desc"
```

#### JavaScript

**`Example request: List tokens`**

```js title="Example request: List tokens" showLineNumbers={false}
const response = await fetch(
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token?limit=50&sort=issued_at&direction=desc",
  {
    method: "GET",
    headers: {
      "X-Auth-Token": "{{access_token}}",
      Accept: "application/json",
    },
  },
);

const { data, meta } = await response.json();
```

#### Response

**`Example response: List tokens`**

```json title="Example response: List tokens" showLineNumbers={false}
{
  "data": [
    {
      "jti": "8f14e45f-ceea-467d-9a3e-0c2f4e6b1a90",
      "type": "simple",
      "name": "My storefront token",
      "channel_ids": [1, 12],
      "allowed_cors_origins": ["https://www.yourstorefront.com"],
      "scopes": [],
      "is_revoked": false,
      "is_expired": false,
      "principal": {
        "id": "l1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o",
        "type": "api_client"
      },
      "issued_at": 1685635176,
      "expires_at": 1885635176
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "count": 1,
      "per_page": 50,
      "current_page": 1,
      "total_pages": 1
    }
  }
}
```

### Revoke a token by ID

The [Revoke a token by ID](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-token-by-jti) endpoint revokes a single token by its `jti`. You do not need the token's JWT string - only the `jti` returned by the list endpoint. A successful request returns `204 No Content`. If no token with that `jti` was issued by your API account in that token family, the request returns `404 Not Found`. Each store can hold a limited number of revoked tokens that have not yet expired. Once that limit is reached, the request returns `422 Unprocessable Entity` until some revoked tokens expire. [Revoke all tokens](#revoke-all-tokens) is not subject to this limit.

#### cURL

**`Example request: Revoke a token by ID`**

```shell title="Example request: Revoke a token by ID" showLineNumbers={false}
curl -sS -X DELETE \
  -H "X-Auth-Token: {{access_token}}" \
  -H "Accept: application/json" \
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token/8f14e45f-ceea-467d-9a3e-0c2f4e6b1a90"
```

#### JavaScript

**`Example request: Revoke a token by ID`**

```js title="Example request: Revoke a token by ID" showLineNumbers={false}
const jti = "8f14e45f-ceea-467d-9a3e-0c2f4e6b1a90";

await fetch(
  `https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token/${jti}`,
  {
    method: "DELETE",
    headers: {
      "X-Auth-Token": "{{access_token}}",
      Accept: "application/json",
    },
  },
);
```

### Revoke all tokens

The [Revoke all tokens](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-all-tokens) endpoint revokes every active token that your API account has issued in the family named by the path segment. Tokens issued by other API accounts are unaffected. A successful request returns `204 No Content`.

> **Warning**
>
> Revoking all tokens invalidates every active token your API account issued in that family. Any integration using one of those tokens will need a new token to continue authenticating.

#### cURL

**`Example request: Revoke all tokens`**

```shell title="Example request: Revoke all tokens" showLineNumbers={false}
curl -sS -X POST \
  -H "X-Auth-Token: {{access_token}}" \
  -H "Accept: application/json" \
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token/revoke-all"
```

#### JavaScript

**`Example request: Revoke all tokens`**

```js title="Example request: Revoke all tokens" showLineNumbers={false}
await fetch(
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token/revoke-all",
  {
    method: "POST",
    headers: {
      "X-Auth-Token": "{{access_token}}",
      Accept: "application/json",
    },
  },
);
```

### Legacy revoke using the full token

The original [Revoke a token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-token) endpoint is still supported. It revokes a token by passing the token's full JWT string in the `Sf-Api-Token` header and returns `200` with `{ "message": "Token revocation is scheduled." }`.

The key difference is what you supply: the legacy endpoint requires the full token string, whereas revoking by `jti` requires only the identifier returned by the list endpoint. For new integrations, prefer revoking by `jti`.

Like revoking by `jti`, the legacy endpoint only accepts tokens your own API account issued. Omitting the `Sf-Api-Token` header returns `422 Unprocessable Entity` with `Sf-Api-Token header has to be provided.`, and passing a token issued by another API account returns `422` with `The token doesn't belong to the client`. The legacy endpoint has its own per-store limit on revoked tokens and returns `422` with `Store cannot revoke more tokens. Limit # of revoked tokens already reached.` once it is reached.

#### cURL

**`Example request: Legacy revoke by full token`**

```shell title="Example request: Legacy revoke by full token" showLineNumbers={false}
curl -sS -X DELETE \
  -H "X-Auth-Token: {{access_token}}" \
  -H "Sf-Api-Token: {{token_to_revoke}}" \
  -H "Accept: application/json" \
  "https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token"
```

#### Response

**`Example response: Legacy revoke by full token`**

```json title="Example response: Legacy revoke by full token" showLineNumbers={false}
{
  "message": "Token revocation is scheduled."
}
```

## Signing customers in

> **Note**
>
> A customer can only sign in on one device. When you run the customer login mutation for a session on a new device, the customer is automatically signed out of the previous session, and any customer access tokens previously issued for the account become invalid. For details, see [Customer session behavior](/developer/docs/storefront/guides/customer-authentication/customer-login#customer-session-behavior).

If you're using the GraphQL Storefront API from a browser, for example, on top of your Stencil storefront, you can use the login mutation to sign in to a customer account with an email address and a password. This will set a session cookie in the browser, which will authenticate the customer account on future requests. You can also perform a login operation by passing the `guestCartEntityID` variable and assigning a cart to a customer.

This mutation is also useful for server-to-server or headless storefront applications that use a customer impersonation token. For example, you can use a sign in form to validate a customer's email address and password. When the mutation returns successfully, you can take the resulting customer ID and store it in a session to pass with the `X-Bc-Customer-Id` header.

As a security best practice, you should inject the email address and password using GraphQL query variables. This prevents the password from being exposed in the query itself. In the [GraphQL Storefront playground](/developer/api-reference/rest/storefront/overview), you can set variables for requests.

![GraphQL Storefront playground query variables](/_fern-img/469c15d50e70fe0d59dc1f4ebf3b3ca4cde30d33a1ce9adc04fafbeb5a3e54a8.webp)

> **Note**
>
> In the customer login mutation, the `result` field is deprecated. Instead, request customer object information.

#### HTTP

**`Login mutation request`**

```http title="Login mutation request" showLineNumbers={false}
  POST https://your_store.example.com/graphql
  Accept: application/json
  Content-Type: application/json
  Authorization: Bearer {token}
```

#### Mutation

**`Login mutation`**

```graphql title="Login mutation"
  mutation Login($email: String!, $pass: String!) {
    login(email: $email, password: $pass) {
      customer {
        entityId
        firstName
        email
        taxExemptCategory
        storeCredit {
          currencyCode
          value
        }
        formFields {
          entityId
          name
        }
      }
    }
  }
```

#### Variables

**`Login mutation variables`**

```json title="Login mutation variables"
  {
    "email": "jane.doe@example.com",
    "pass": "xxxxxxxxxxxxxxxxx"
  }
```

#### Response

**`Login mutation response`**

```json title="Login mutation response"
  {
    "data": {
      "login": {
        "customer": {
          "entityId": 378,
          "firstName": "Jane",
          "email": "jane.doe@example.com",
          "customerGroupId": 7,
          "taxExemptCategory": "",
          "storeCredit": [
            {
              "currencyCode": "USD",
              "value": 0
            }
          ],
          "formFields": [
            {
              "entityId": 26,
              "name": "Birthday"
            }
          ]
        }
      }
    }
  }
```

## Signing customers out

You can use the logout mutation to sign out of a customer account. If you're using a vanilla storefront token, future requests in that shopper's session will be from the perspective of an anonymous shopper.

**`Example query: logout mutation`**

```graphql title="Example query: logout mutation" showLineNumbers={false}
mutation Logout {
  logout {
    result
  }
}
```

> **Note**
>
> If you're signed out, the system reverts to a "guest" version of the cart, allowing a guest shopper to view their cart once logged out.

## Further reading

* [GraphQL Storefront API overview](/developer/docs/storefront/guides/graphql-storefront-api)
* [GraphQL Storefront API reference](/developer/api-reference/rest/storefront/overview)
* [GraphQL Storefront playground](/developer/api-reference/rest/storefront/overview)
* [Authentication and example requests: GraphQL Storefront API](/developer/docs/overview/api-fundamentals/api-accounts#api-accounts)
* [Guide to API accounts: OAuth scopes](/developer/docs/overview/api-fundamentals/api-accounts#token-creation-scopes)
* [Multi-storefront overview](/developer/docs/admin/multi-storefront)
* [CORS (Mozilla)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

### Endpoints

* [Create a storefront token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/create-token)
* [Create a private token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/private-api-token/create-private-token)
* [Create a customer impersonation token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens)
* [List tokens](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/list-tokens)
* [Revoke a token by ID](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-token-by-jti)
* [Revoke all tokens](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-all-tokens)
* [Revoke a token (legacy, using the full token)](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/revoke-token)
* [Customer Login API](/developer/api-reference/rest/admin/authentication-apis/customer-login-api)