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

# Authentication

> How to authenticate against the CLOB Trading API: the three auth tiers, API-key lifecycle, permissions, and short-lived WebSocket tokens.

The CLOB API has three auth tiers. Each route picks exactly one.

| Tier   | Used by             | Required credentials                                    |
| ------ | ------------------- | ------------------------------------------------------- |
| **L0** | Anyone              | None                                                    |
| **L1** | Logged-in web users | `Authorization: Bearer <privy-jwt>`                     |
| **L2** | Bots or users       | Privy JWT **or** `fs-api-key` + `fs-api-secret` headers |

* **L0** — all `GET /v1/markets/*` routes. No auth.
* **L1** — API-key management (`/v1/auth/api-keys*`). Privy JWT only — you cannot manage keys with a key.
* **L2** — trading and private reads (`/v1/orders*`, `/v1/fills`, `/v1/positions`, `/v1/auth/ws-token`). Accepts a JWT **or** an API-key pair.

## API keys

API keys are how bots authenticate. Each key is bound to the wallet that created
it and carries a set of permissions.

### Headers

Send both headers on every L2 request:

```http theme={null}
fs-api-key: fs_key_abc123...
fs-api-secret: fs_secret_...
```

### Lifecycle

All key-management routes require an **L1 Privy JWT** (not an API key).

<Steps>
  <Step title="Create a key">
    ```bash theme={null}
    curl -X POST https://api.foresight.now/v1/auth/api-keys \
      -H "Authorization: Bearer $PRIVY_JWT" \
      -H "Content-Type: application/json" \
      -d '{ "permissions": ["read", "trade"] }'
    ```

    ```json theme={null}
    { "key_id": "fs_key_abc123...", "secret": "fs_secret_..." }
    ```

    <Warning>
      The `secret` is returned **only once**. Store it immediately — it cannot be
      retrieved later, only rotated. The server keeps a bcrypt hash, never the
      plaintext.
    </Warning>
  </Step>

  <Step title="List your keys">
    `GET /v1/auth/api-keys` returns key metadata (never secrets), including
    `permissions` and `last_used_at`.
  </Step>

  <Step title="Rotate a secret">
    `POST /v1/auth/api-keys/{key_id}/regenerate` issues a new secret. The old one
    stops working immediately.
  </Step>

  <Step title="Revoke a key">
    `DELETE /v1/auth/api-keys/{key_id}` soft-revokes the key. It stops working
    immediately.
  </Step>
</Steps>

If `permissions` is omitted on creation it defaults to `["read"]`.

## Permissions

Permissions gate L2 routes. They are checked only for API keys — a Privy JWT
user implicitly has all permissions.

| Permission | Unlocks                                                                              |
| ---------- | ------------------------------------------------------------------------------------ |
| `read`     | All list/get endpoints: `GET /v1/orders`, `/v1/fills`, `/v1/positions`               |
| `trade`    | `POST /v1/orders`, `POST /v1/orders/cancel`, `POST /v1/orders/{hash}/cancel-onchain` |

## Wallet binding

A key can only trade for the wallet that created it. `POST /v1/orders` also
checks that each order's EIP-712 **`signer` equals the authenticated wallet** —
a mismatch returns a `WALLET_MISMATCH` error inline for that order. You cannot
place orders on behalf of another wallet.

## WebSocket tokens

Private WebSocket channels require a short-lived token, **not** your API-key
headers.

```bash theme={null}
curl -X POST https://api.foresight.now/v1/auth/ws-token \
  -H "fs-api-key: $FS_API_KEY" \
  -H "fs-api-secret: $FS_API_SECRET"
```

```json theme={null}
{ "token": "deadbeef...", "expires_in": 60 }
```

* **Single-use.** The token is consumed by the first WS connection that uses it.
* **60-second TTL.** Re-issue one per connection / reconnect.
* Pass it as a query param: `wss://api.foresight.now/v1/ws?token=<token>`.

<Info>
  WS tokens authenticate the WebSocket upgrade only. They do **not** authenticate
  REST calls — REST always uses a JWT or API-key headers.
</Info>
