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

# Authentication

> Cuadra AI supports JWT session tokens for frontend apps and M2M OAuth 2.0 for backend services. All requests require Bearer token authentication.

## Which Method Should I Use?

| Scenario          | Method        | Why                                           |
| ----------------- | ------------- | --------------------------------------------- |
| React/Next.js app | JWT Sessions  | User is logged in, token comes from auth flow |
| Mobile app        | JWT Sessions  | Same as web—user session based                |
| Backend service   | M2M OAuth 2.0 | No user session, server-to-server calls       |
| CLI tool          | M2M OAuth 2.0 | Automated scripts, no browser                 |
| Cron job          | M2M OAuth 2.0 | Background tasks, no user context             |

***

## JWT Session Tokens

For frontend apps where users authenticate via Stytch B2B (magic link, SSO, or password).

### Usage

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.cuadra.ai/v1/chats \
    -H "Authorization: Bearer $SESSION_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "modelId": "model_123",
      "messages": [{"role": "user", "content": "Hello"}]
    }'
  ```

  ```python Python theme={null}
  import httpx

  session_token = get_session_token()  # From your auth system

  response = httpx.post(
      "https://api.cuadra.ai/v1/chats",
      headers={"Authorization": f"Bearer {session_token}"},
      json={
          "modelId": "model_123",
          "messages": [{"role": "user", "content": "Hello"}]
      }
  )
  print(response.json())
  ```

  ```typescript JavaScript theme={null}
  const sessionToken = await getSessionToken(); // From your auth system

  const response = await fetch('https://api.cuadra.ai/v1/chats', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${sessionToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      modelId: 'model_123',
      messages: [{ role: 'user', content: 'Hello' }]
    })
  });
  const data = await response.json();
  ```
</CodeGroup>

### With React UI Kit

```tsx theme={null}
import { CuadraChat } from '@cuadra-ai/uikit';

function App() {
  const sessionToken = useSessionToken();

  return (
    <CuadraChat
      connection={{ baseUrl: "https://api.cuadra.ai", sessionToken }}
      chat={{ modelId: "model_123" }}
    />
  );
}
```

***

## M2M OAuth 2.0

For backend services using OAuth 2.0 Client Credentials flow.

### Step 1: Get Credentials

Create M2M client credentials in Dashboard → **Settings** → **API Access**. Assign scopes based on what the service needs.

### Step 2: Exchange for Token

Exchange your client credentials for an access token:

**Token Endpoint:**

```
https://auth.cuadra.ai/v1/oauth2/token
```

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://auth.cuadra.ai/v1/oauth2/token" \
    -H "Content-Type: application/json" \
    -d '{
      "client_id": "m2m-client-xxx",
      "client_secret": "secret-xxx",
      "grant_type": "client_credentials"
    }'
  ```

  ```python Python theme={null}
  import httpx

  response = httpx.post(
      "https://auth.cuadra.ai/v1/oauth2/token",
      json={
          "client_id": "m2m-client-xxx",
          "client_secret": "secret-xxx",
          "grant_type": "client_credentials"
      }
  )
  token_data = response.json()
  access_token = token_data["access_token"]  # Valid for 1 hour
  ```

  ```typescript Node.js theme={null}
  const response = await fetch('https://auth.cuadra.ai/v1/oauth2/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: 'm2m-client-xxx',
      client_secret: 'secret-xxx',
      grant_type: 'client_credentials'
    })
  });
  const { access_token } = await response.json(); // Valid for 1 hour
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}
```

### Step 3: Use the Token

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.cuadra.ai/v1/chats \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"modelId": "model_123", "messages": [{"role": "user", "content": "Hello"}]}'
  ```

  ```python Python theme={null}
  import httpx

  response = httpx.post(
      "https://api.cuadra.ai/v1/chats",
      headers={"Authorization": f"Bearer {access_token}"},
      json={"modelId": "model_123", "messages": [{"role": "user", "content": "Hello"}]}
  )
  print(response.json())
  ```

  ```typescript Node.js theme={null}
  const response = await fetch('https://api.cuadra.ai/v1/chats', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      modelId: 'model_123',
      messages: [{ role: 'user', content: 'Hello' }]
    })
  });
  const data = await response.json();
  ```
</CodeGroup>

### Token Caching

M2M tokens expire after 1 hour. Cache and refresh before expiry:

<CodeGroup>
  ```python Python theme={null}
  from datetime import datetime, timedelta
  import httpx

  class TokenManager:
      TOKEN_URL = "https://auth.cuadra.ai/v1/oauth2/token"
      
      def __init__(self, client_id, client_secret):
          self.client_id = client_id
          self.client_secret = client_secret
          self._token = None
          self._expires_at = None

      async def get_token(self):
          if self._token and self._expires_at > datetime.utcnow():
              return self._token
          
          async with httpx.AsyncClient() as client:
              response = await client.post(
                  self.TOKEN_URL,
                  json={
                      "client_id": self.client_id,
                      "client_secret": self.client_secret,
                      "grant_type": "client_credentials"
                  }
              )
              data = response.json()
              self._token = data["access_token"]
              # Refresh 5 minutes before expiry
              self._expires_at = datetime.utcnow() + timedelta(seconds=data["expires_in"] - 300)
              return self._token
  ```

  ```typescript Node.js theme={null}
  class TokenManager {
    static TOKEN_URL = 'https://auth.cuadra.ai/v1/oauth2/token';
    
    constructor(clientId, clientSecret) {
      this.clientId = clientId;
      this.clientSecret = clientSecret;
      this.token = null;
      this.expiresAt = null;
    }

    async getToken() {
      if (this.token && this.expiresAt > Date.now()) {
        return this.token;
      }

      const response = await fetch(TokenManager.TOKEN_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          client_id: this.clientId,
          client_secret: this.clientSecret,
          grant_type: 'client_credentials'
        })
      });

      const data = await response.json();
      this.token = data.access_token;
      // Refresh 5 minutes before expiry
      this.expiresAt = Date.now() + (data.expires_in - 300) * 1000;
      return this.token;
    }
  }
  ```
</CodeGroup>

***

## Scopes

| Scope                  | Permission                            |
| ---------------------- | ------------------------------------- |
| `chat:invoke`          | Create chat completions (billable)    |
| `chat:read`            | Read chat history                     |
| `chat:write`           | Create, update, delete chats          |
| `models:read`          | List and view models                  |
| `models:write`         | Create, update, delete models         |
| `datasets:read`        | View datasets                         |
| `datasets:write`       | Create, update, delete datasets       |
| `files:read`           | View and download files               |
| `files:write`          | Upload, update, delete files          |
| `particles:read`       | View particles                        |
| `particles:write`      | Create, update, delete particles      |
| `system-prompts:read`  | View system prompts                   |
| `system-prompts:write` | Create, update, delete system prompts |
| `usage:read`           | View usage and credit balance         |
| `org:admin`            | Organization admin access             |

Request minimum necessary scopes for each service.

***

## Security Best Practices

1. **Never expose tokens in client-side code** — Use a backend proxy for production
2. **Store secrets in environment variables** — Not in code or version control
3. **Rotate M2M credentials every 90 days** — Dashboard → Settings → API Access
4. **Use HTTPS only** — The API rejects HTTP requests
5. **Scope credentials minimally** — Only request permissions you need

***

## Troubleshooting

| Error              | Cause                    | Solution                                 |
| ------------------ | ------------------------ | ---------------------------------------- |
| `401 Unauthorized` | Invalid or expired token | Refresh M2M token; verify JWT session    |
| `403 Forbidden`    | Missing scope            | Request credentials with required scopes |
| `Token Expired`    | M2M token past 1 hour    | Request new token                        |

***

## FAQ

### Can I use API keys instead of OAuth?

No. Cuadra AI uses OAuth 2.0 for server-side authentication. M2M credentials function similarly to API keys but require a token exchange step for security.

### How do I revoke a compromised token?

For M2M: Rotate the client secret in Dashboard → **Settings** → **API Access**. For JWT: Revoke the user session in your Stytch dashboard.

### What's the token lifetime?

M2M access tokens expire after 1 hour. JWT session tokens follow your Stytch session configuration (typically 24 hours to 30 days).

### Do I need separate credentials for staging vs production?

Recommended. Create separate M2M clients per environment to isolate access and simplify rotation.

***

## Related

<CardGroup cols="2">
  <Card title="API Overview" icon="book" href="/api-reference/overview">
    Base URL, rate limits
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/api-reference/errors">
    Error handling
  </Card>
</CardGroup>
