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

# Authentication

> API keys, Bearer token format, client secrets, rate limits, and retry guidance.

FaceSign uses API keys to authenticate all requests. Every request — sandbox or production — goes to the same base URL:

```
https://api.facesign.ai
```

What distinguishes sandbox from production is the key prefix and the quotas attached to it:

| Environment | Key prefix    | Rate limit    | Data retention |
| ----------- | ------------- | ------------- | -------------- |
| Sandbox     | `sk_test_...` | 100 req/min   | 7 days         |
| Production  | `sk_live_...` | 1,000 req/min | Per contract   |

If you don't have a key yet, see the [Build with AI](/docs/mcp#prerequisites) quickstart.

## Using API keys

Pass the key as a Bearer token in the Authorization header:

<Tabs>
  <Tab title="bash">
    ```bash wrap theme={null}
    curl https://api.facesign.ai/sessions \
      -H "Authorization: Bearer sk_test_..."
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript wrap theme={null}
    import Client from '@facesignai/api'

    const client = new Client({
      auth: process.env.FACESIGN_API_KEY,
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python wrap theme={null}
    import os
    import facesignai

    client = facesignai.Client(
        auth=os.environ.get("FACESIGN_API_KEY")
    )
    ```
  </Tab>
</Tabs>

The key prefix is the only thing that changes between environments — swap `sk_test_...` for `sk_live_...` and the same code hits production.

## Client secrets

Each session generates a single-use client secret (prefixed `cs...`) containing an embedded URL, expiration timestamp, and authentication credentials for that specific session. Client secrets are safe to expose to the frontend. API keys must never leave the server.

```javascript title="Create session on backend, pass secret to frontend" wrap theme={null}
import Client from '@facesignai/api'

const client = new Client({
  auth: process.env.FACESIGN_API_KEY,
})

const { session, clientSecret } = await client.session.create({
  clientReferenceId: 'user-123',
  metadata: {},
  flow: [...],
})

// Send clientSecret.url to your frontend
// The user opens this URL to complete verification
return { url: clientSecret.url }
```

## Security rules

<Warning>
  **API Key Security**

  Your API keys grant access to sensitive data and actions. Handle them carefully.
</Warning>

* **API keys**: server-side only. Never embed in client code, public repos, or browser environments.
* **Client secrets**: safe for the frontend, but single-use and short-lived.
* For frontend integration, create sessions on your backend and pass only the client secret URL to the user.

## Environments

| Feature         | Sandbox     | Production    |
| --------------- | ----------- | ------------- |
| Key prefix      | `sk_test_`  | `sk_live_`    |
| Rate limit      | 100 req/min | 1,000 req/min |
| Data retention  | 7 days      | Per contract  |
| Full API access | Yes         | Yes           |

Both environments share the same base URL (`https://api.facesign.ai`). The key prefix is what routes your request into the right environment.

## Rate limits

| Environment             | Limit                     |
| ----------------------- | ------------------------- |
| Sandbox (`sk_test_`)    | 100 requests per minute   |
| Production (`sk_live_`) | 1,000 requests per minute |

When rate limited, the API returns `429` with a `rate_limit_exceeded` code. Implement exponential backoff with jitter: 1s, 2s, 4s, and so on.

### Retry guidance

* **Network errors and `5xx` responses**: retry with exponential backoff (up to 3 attempts).
* **`429` responses**: respect the rate limit window, then retry.
* **`4xx` responses (except 429)**: do not retry. Fix the request.
* **Media download failures**: re-fetch the session to get a fresh URL (they expire after 15 minutes).
