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

# Authentication

> OAuth 2.0 authentication flows for accessing the OrderProtection API.

All API access is authenticated using OAuth 2.0. Your app receives access tokens that are scoped to the specific permissions each merchant granted during installation.

**Base URL:**

```
https://api.production.orderprotection.com
```

## Authorization Code Flow

This is the primary flow for apps installed by merchants. When a merchant installs your app from the marketplace, OrderProtection delivers an authorization code to your redirect URI. You exchange this code for access and refresh tokens.

### 1. Merchant installs your app

When a merchant clicks **Install** on your app, they review and approve the requested scopes. OrderProtection then sends an authorization code to your redirect URI:

```
https://yourapp.example.com/callback?code=a1b2c3d4e5f6&state=xyz
```

| Parameter | Description                                                    |
| --------- | -------------------------------------------------------------- |
| `code`    | The authorization code to exchange for tokens                  |
| `state`   | The state parameter you provided (if any), for CSRF protection |

### 2. Exchange code for tokens

Exchange the authorization code for an access token and refresh token:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.production.orderprotection.com/v1/oauth/token \
    -H "Content-Type: application/json" \
    -d '{
      "grant_type": "authorization_code",
      "client_id": "op_app_fc6767b5694ba053...",
      "client_secret": "op_secret_8915e130...",
      "code": "a1b2c3d4e5f6",
      "redirect_uri": "https://yourapp.example.com/callback"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api.production.orderprotection.com/v1/oauth/token",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        grant_type: "authorization_code",
        client_id: "op_app_fc6767b5694ba053...",
        client_secret: "op_secret_8915e130...",
        code: "a1b2c3d4e5f6",
        redirect_uri: "https://yourapp.example.com/callback",
      }),
    }
  );
  const tokens = await response.json();
  ```
</CodeGroup>

### Token response

```json theme={null}
{
  "access_token": "op_at_af444635983c457a5e...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "op_rt_27f3894986c211168b...",
  "scope": "read_orders read_claims read_store"
}
```

| Field           | Description                                                           |
| --------------- | --------------------------------------------------------------------- |
| `access_token`  | Bearer token for API calls. Prefixed with `op_at_`.                   |
| `token_type`    | Always `Bearer`.                                                      |
| `expires_in`    | Token lifetime in seconds (3600 = 1 hour).                            |
| `refresh_token` | Long-lived token to obtain new access tokens. Prefixed with `op_rt_`. |
| `scope`         | Space-separated list of granted scopes.                               |

### PKCE support

The authorization code flow supports PKCE (Proof Key for Code Exchange) with the S256 method for enhanced security. If a `code_challenge` was provided during authorization, you must include the `code_verifier` when exchanging the code:

```json theme={null}
{
  "grant_type": "authorization_code",
  "client_id": "op_app_...",
  "client_secret": "op_secret_...",
  "code": "a1b2c3d4e5f6",
  "redirect_uri": "https://yourapp.example.com/callback",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
```

## Client Credentials Flow

Use this flow for server-to-server access when no user interaction is needed. This is useful for background jobs, data sync, or admin operations.

<Note>
  The client credentials flow requires an **existing installation** on the target store. A merchant must have already installed your app (granting scopes) before you can use this flow. The `store_id` parameter tells OrderProtection which installation to use for determining your granted scopes.
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.production.orderprotection.com/v1/oauth/token \
    -H "Content-Type: application/json" \
    -d '{
      "grant_type": "client_credentials",
      "client_id": "op_app_fc6767b5694ba053...",
      "client_secret": "op_secret_8915e130...",
      "store_id": "store_abc123",
      "scopes": ["read_orders", "read_claims"]
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api.production.orderprotection.com/v1/oauth/token",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        grant_type: "client_credentials",
        client_id: "op_app_fc6767b5694ba053...",
        client_secret: "op_secret_8915e130...",
        store_id: "store_abc123",
        scopes: ["read_orders", "read_claims"],
      }),
    }
  );
  const tokens = await response.json();
  ```
</CodeGroup>

<Note>
  The `scopes` parameter is optional and acts as a filter. If omitted, the token will include all scopes granted by the merchant during installation.
</Note>

## Refreshing tokens

Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without requiring the merchant to re-authorize:

```bash theme={null}
curl -X POST https://api.production.orderprotection.com/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "refresh_token",
    "client_id": "op_app_fc6767b5694ba053...",
    "client_secret": "op_secret_8915e130...",
    "refresh_token": "op_rt_27f3894986c211168b..."
  }'
```

The response includes a new access token and a new refresh token. Always store and use the latest refresh token — previous ones are rotated.

## Revoking tokens

When a merchant disconnects your app or you need to invalidate tokens, revoke them explicitly:

```bash theme={null}
curl -X POST https://api.production.orderprotection.com/v1/oauth/revoke \
  -H "Content-Type: application/json" \
  -d '{
    "token": "op_at_af444635983c457a5e...",
    "client_id": "op_app_fc6767b5694ba053...",
    "client_secret": "op_secret_8915e130...",
    "token_type_hint": "access_token"
  }'
```

| Parameter         | Required | Description                              |
| ----------------- | -------- | ---------------------------------------- |
| `token`           | Yes      | The token to revoke                      |
| `client_id`       | Yes      | Your app's client ID                     |
| `client_secret`   | Yes      | Your app's client secret                 |
| `token_type_hint` | No       | Either `access_token` or `refresh_token` |

## Using access tokens

Include the access token in the `Authorization` header of every API request:

```bash theme={null}
curl -X GET https://api.production.orderprotection.com/v1/orders \
  -H "Authorization: Bearer op_at_af444635983c457a5e..."
```

<Warning>
  Access tokens are scoped to the permissions the merchant granted. If you attempt to access an endpoint that requires a scope the merchant did not grant, you will receive a `403 Forbidden` response.
</Warning>

## Token prefixes

OrderProtection tokens use prefixes for easy identification:

| Prefix       | Type          |
| ------------ | ------------- |
| `op_at_`     | Access token  |
| `op_rt_`     | Refresh token |
| `op_app_`    | Client ID     |
| `op_secret_` | Client secret |
