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

# Authentication

> How to authenticate with the Shoppex Developer API

<Note>
  If you are using **Claude Code, Codex, or an internal automation script** for themes, use an **API key** with scopes `themes.read themes.write`. Use OAuth2 only if you are building an external app that other merchants install.
</Note>

## Authentication modes

Shoppex supports two Bearer token modes for the Dev API:

* API keys for server-to-server integrations you manage directly
* OAuth2 access tokens for installable apps and third-party connectors

Your own warehouse worker uses an API key. A third-party ERP or CRM uses OAuth2, so the merchant clicks "Connect Shoppex" instead of pasting a raw key.

## Quick decision

Use this shortcut:

* **I run the tool myself** -> API key
* **I am building an installable third-party app** -> OAuth2

Claude Code editing your own theme? API key. Codex running your internal deployment helper? API key. ERP integration for many merchants? OAuth2.

<Tabs>
  <Tab title="API Keys">
    Shoppex uses API keys for authentication. Each key belongs to exactly one shop and can be limited with scopes.

    ### Creating an API key

    <Steps>
      <Step title="Open Dashboard">
        Go to [dashboard.shoppex.io](https://dashboard.shoppex.io) and log in.
      </Step>

      <Step title="Navigate to Settings">
        Go to **Settings** in the sidebar.
      </Step>

      <Step title="Generate Key">
        In the **Developer API** section, click **Generate New API Key**.
      </Step>

      <Step title="Choose access">
        Pick the smallest scope set your integration needs. Start from a preset, or choose
        **Custom** to select scopes one resource at a time. To change the scopes of a key
        later, use **Edit Scopes** in its menu.

        Common scope combinations:

        | Integration            | Scopes                              |
        | ---------------------- | ----------------------------------- |
        | Fulfillment / ERP sync | `orders.read`                       |
        | Chargeback tooling     | `disputes.read`                     |
        | Report automation      | `analytics.read`, `analytics.write` |
        | Theme development      | `themes.read`, `themes.write`       |
        | Catalog sync           | `products.read`                     |
        | CRM sync               | `customers.read`                    |
        | Webhook management     | `webhooks.read`, `webhooks.write`   |
      </Step>

      <Step title="Copy Key">
        Copy your API key immediately. It starts with `shx_` and will not be shown again.

        <Warning>
          Store your API key securely. If compromised, regenerate it immediately.
        </Warning>
      </Step>
    </Steps>

    ### Using your API key

    Include the API key in the `Authorization` header with the `Bearer` scheme:

    <CodeGroup>
      ```bash cURL theme={"system"}
      curl -X GET https://api.shoppex.io/dev/v1/invoices \
        -H "Authorization: Bearer shx_your_api_key_here"
      ```

      ```typescript TypeScript theme={"system"}
      const response = await fetch('https://api.shoppex.io/dev/v1/invoices', {
        headers: {
          'Authorization': 'Bearer shx_your_api_key_here'
        }
      });
      ```

      ```python Python theme={"system"}
      import requests

      response = requests.get(
          'https://api.shoppex.io/dev/v1/invoices',
          headers={'Authorization': 'Bearer shx_your_api_key_here'}
      )
      ```

      ```php PHP theme={"system"}
      $ch = curl_init('https://api.shoppex.io/dev/v1/invoices');
      curl_setopt($ch, CURLOPT_HTTPHEADER, [
          'Authorization: Bearer shx_your_api_key_here'
      ]);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      $response = curl_exec($ch);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="OAuth2">
    OAuth2 is the better fit when you want an external app to connect to a shop without asking the merchant for a raw API key.

    ### Creating an OAuth app

    1. Log in to [dashboard.shoppex.io](https://dashboard.shoppex.io)
    2. Go to your Dev API settings
    3. Create an OAuth app with:
       * a name
       * one or more redirect URIs
       * the scopes the app can request
    4. Copy the `client_id` and `client_secret`

    For example: App name `ERP Connector`, redirect URI `https://erp.example.com/oauth/callback`, scopes `orders.read customers.read`.

    ### Authorize endpoint

    The merchant browser is redirected to:

    ```bash theme={"system"}
    GET /dev/v1/oauth/authorize?response_type=code&client_id=shoc_xxx&redirect_uri=https%3A%2F%2Ferp.example.com%2Foauth%2Fcallback&scope=orders.read%20customers.read&state=sync_42
    ```

    If the user is logged in and allowed to approve integrations for the active shop, Shoppex first shows a Shoppex-hosted approval screen.

    After the merchant clicks **Approve and continue**, Shoppex issues a short-lived code and redirects back to your app.

    ```bash theme={"system"}
    https://erp.example.com/oauth/callback?code=shoa_abc123&state=sync_42
    ```

    Important:

    * the approval step is intentionally interactive, so a third-party site cannot silently mint OAuth codes for a logged-in merchant
    * the approval submit is protected with a Shoppex CSRF token behind the hosted approval page

    ### Token endpoint

    Exchange that code for an access token:

    ```bash theme={"system"}
    curl https://api.shoppex.io/dev/v1/oauth/token \
      -X POST \
      -H "Authorization: Basic $(printf 'shoc_xxx:shcs_xxx' | base64)" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=authorization_code&code=shoa_abc123&redirect_uri=https%3A%2F%2Ferp.example.com%2Foauth%2Fcallback"
    ```

    Example response:

    ```json theme={"system"}
    {
      "access_token": "shpat_xxx",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "shprt_xxx",
      "scope": "orders.read customers.read"
    }
    ```

    Refresh flow:

    ```bash theme={"system"}
    curl https://api.shoppex.io/dev/v1/oauth/token \
      -X POST \
      -H "Content-Type: application/json" \
      -d '{
        "grant_type": "refresh_token",
        "refresh_token": "shprt_xxx",
        "client_id": "shoc_xxx",
        "client_secret": "shcs_xxx"
      }'
    ```

    ### Using the access token

    After the exchange, call normal Dev API endpoints with the OAuth access token:

    ```bash theme={"system"}
    curl https://api.shoppex.io/dev/v1/orders \
      -H "Authorization: Bearer shpat_your_access_token_here"
    ```

    Important:

    * OAuth token responses follow the normal OAuth2 JSON format
    * normal `/dev/v1/*` resource calls still use the standard Shoppex Dev API response envelope
    * scopes still apply exactly the same way as for API keys
  </Tab>
</Tabs>

## Scopes

Scopes follow a `resource.action` format, for example `products.read`, `orders.write`, or `themes.read`. Use `*` for full access.

The most common mistake here is giving a key more scopes than it needs. A catalog sync must not also get `customers.write`. A reporting tool only needs read scopes. Grant the minimum and expand later if needed.

Available scopes fall into three groups:

### Commerce scopes

* `orders.read`
* `disputes.read`
* `subscriptions.write`
* `payment_links.write`
* `products.read`

### Customer & analytics scopes

* `customers.write`
* `analytics.read`
* `analytics.write`
* `affiliates.read`
* `affiliates.write`

### Platform & security scopes

* `webhooks.write`
* `themes.read`
* `themes.write`
* `licenses.read`
* `licenses.write`
* `security.read`
* `security.write`
* `store.read`
* `store.write`
* `*` for full access

OAuth2 access tokens use these same Dev API scopes. For instance, your app requests `orders.read customers.read`, the merchant approves, and the exchanged `shpat_...` token can then only read orders and customers.

### Inspecting the active key

Use `GET /me/capabilities` to inspect the active key at runtime:

```bash theme={"system"}
curl https://api.shoppex.io/dev/v1/me/capabilities \
  -H "Authorization: Bearer shx_your_api_key_here"
```

This response tells you:

* which scopes are active
* whether the key has full access
* which resource groups are readable or writable
* which payment methods are enabled for the shop
* which gated Dev API features are enabled for the shop

Notably, `enabledPaymentMethods` tells you whether gateways like Stripe, PayPal, or your connected crypto provider (OxaPay, Cryptomus, NowPayments) are active. `features.nativeCryptoCheckout` tells you which non-custodial native crypto gateways are available.

If you authenticated with OAuth2, the capabilities response also tells you which OAuth client issued the current access token.

## Key format

| Prefix   | Description                                  |
| -------- | -------------------------------------------- |
| `shx_`   | Shoppex API Key (32 characters after prefix) |
| `shoc_`  | OAuth client id                              |
| `shcs_`  | OAuth client secret                          |
| `shoa_`  | OAuth authorization code                     |
| `shpat_` | OAuth access token                           |
| `shprt_` | OAuth refresh token                          |

Example: `shx_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6`

## Error responses

### Missing or invalid key

```json theme={"system"}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid API key. Expected: Authorization: Bearer shx_...",
    "doc_url": "https://docs.shoppex.io/api-reference/errors"
  }
}
```

### Shop suspended

```json theme={"system"}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Your shop has been suspended.",
    "doc_url": "https://docs.shoppex.io/api-reference/errors"
  }
}
```

### Missing scope

```json theme={"system"}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Missing required API scope.",
    "doc_url": "https://docs.shoppex.io/api-reference/errors"
  }
}
```

## Regenerating keys

If an API key is compromised:

1. Go to **Settings** in your dashboard
2. Revoke the affected key
3. Create a new key with the smallest required scopes
4. Update your integration with the new key

The old key is immediately invalidated. All requests using it will fail with `401 Unauthorized`.

## Best practices

### Use least privilege scopes

Give every integration only the scopes it really needs. A reporting tool needs `products.read` and `payments.read`, not `*`.

### Handle rate-limit headers

Read `X-RateLimit-Remaining` and `Retry-After`. When remaining hits `0`, back off until the reset window passes instead of hammering the API.

### Forward a request id

If your worker is processing `job_123`, send `X-Request-Id: job_123` so your logs and Shoppex support traces line up.

### Use environment variables

Never hardcode API keys in your source code.

```bash theme={"system"}
export SHOPPEX_API_KEY=shx_your_api_key_here
```

```typescript theme={"system"}
const apiKey = process.env.SHOPPEX_API_KEY;
```

### Keep keys server-side

Never expose your API key in client-side code (browsers, mobile apps). Make API calls from your backend server.

### Use HTTPS only

Always use HTTPS when making API requests to prevent key interception.

### Monitor usage

Check your dashboard regularly for unusual API activity.
