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

# Authentication

Authenticate your API requests with an API key.

## API Keys

All API requests require an API key passed via the `X-API-Key` header.

```bash
curl -X POST https://fingerprintiq.com/v1/identify \
  -H "Content-Type: application/json" \
  -H "X-API-Key: fiq_live_your_api_key" \
  -d '{"signals": {...}}'
```

## Key Types

Your API key. Two formats are supported:

  - **`fiq_live_`** — Live key for production traffic. Counts toward your monthly quota.
  - **`fiq_test_`** — Test key for development and staging. Full functionality, excluded from monthly quota.

Use test keys (`fiq_test_`) in your CI/CD pipeline, staging environments, and local development. This keeps your production quota clean and makes it easy to distinguish production traffic in your dashboard.

## Creating API Keys

**Sign in to the dashboard**

Go to [fingerprintiq.com/dashboard](https://fingerprintiq.com/dashboard) and sign in.
  **Navigate to API Keys**

Select **API Keys** from the left sidebar.
  **Create a new key**

Click **Create Key**, give it a descriptive name (e.g., "Production Web App"), and select the key type.
  **Copy the key immediately**

The full key value is shown only once at creation. Copy it to your secrets manager before closing the dialog.

API keys are shown only once at creation time. If you lose a key, revoke it and create a new one — there is no way to retrieve the original value.

## CLI / AI Agent Authentication (Device Code Flow)

If you're building a CLI, an AI agent integration, or any non-browser tool that needs to provision an API key on the user's behalf, use the device code flow instead of asking the user to copy/paste from the dashboard. It's modeled on OAuth 2.0 device authorization (RFC 8628).

**How it works:**

1. Your tool asks FingerprintIQ for a device code.
2. Your tool prints a short verification URL to the user.
3. The user opens the URL, signs in, picks a project, and clicks **Authorize**.
4. Your tool polls for the key and receives it exactly once.

The issued key is a standard `fiq_live_` key scoped to the project the user selected. It appears in Dashboard → API Keys and can be revoked there at any time.

### 1. Request a device code

```bash curl
curl -X POST https://fingerprintiq.com/api/cli/device-code \
  -H "Content-Type: application/json" \
  -d '{"client_name": "my-cli"}'
```
```javascript Node
const res = await fetch("https://fingerprintiq.com/api/cli/device-code", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ client_name: "my-cli" }),
});
const grant = await res.json();
```
```python Python
import requests
grant = requests.post(
    "https://fingerprintiq.com/api/cli/device-code",
    json={"client_name": "my-cli"},
).json()
```

Human-readable name shown to the user on the consent screen (e.g. `claude-code`, `cursor`, `my-cli`). Capped at 64 characters.

**Response:**

```json
{
  "device_code": "…64-char opaque token…",
  "user_code": "ABCD-EFGH",
  "verification_url": "https://fingerprintiq.com/cli/auth?code=ABCD-EFGH",
  "expires_in": 600,
  "interval": 3
}
```

**device_code**

Opaque token your tool holds on to. Used to poll for the issued key. Never shown to the user.
**user_code**

Short, human-readable code (8 chars, dashed). Shown to the user so they can confirm it matches what's on the consent screen.
**verification_url**

URL your tool should open (or print) for the user.
**expires_in**

Seconds until the grant expires. Always 600 (10 minutes).
**interval**

Minimum seconds between token poll requests.

### 2. Direct the user to authorize

Print the `verification_url` and `user_code` and wait:

```
Open this URL in your browser to authorize:
  https://fingerprintiq.com/cli/auth?code=ABCD-EFGH

Verification code: ABCD-EFGH
Waiting for authorization…
```

On the consent page the user will sign in (if needed), pick which project the key should be scoped to, and confirm that the verification code matches.

### 3. Poll for the API key

While waiting, poll `POST /api/cli/token` with the `device_code` every `interval` seconds:

```bash curl
curl -X POST https://fingerprintiq.com/api/cli/token \
  -H "Content-Type: application/json" \
  -d '{"device_code": "…"}'
```
```javascript Node
async function pollForKey(deviceCode, interval) {
  while (true) {
    const res = await fetch("https://fingerprintiq.com/api/cli/token", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ device_code: deviceCode }),
    });
    const data = await res.json();
    if (data.status === "authorized") return data.api_key;
    if (res.status === 410) throw new Error(`Grant ${data.status}`);
    await new Promise((r) => setTimeout(r, interval * 1000));
  }
}
```
```python Python
import time, requests

def poll_for_key(device_code, interval):
    while True:
        r = requests.post(
            "https://fingerprintiq.com/api/cli/token",
            json={"device_code": device_code},
        )
        data = r.json()
        if data.get("status") == "authorized":
            return data["api_key"]
        if r.status_code == 410:
            raise RuntimeError(f"Grant {data.get('status')}")
        time.sleep(interval)
```

**Poll responses:**

| Status | HTTP | Meaning |
|--------|------|---------|
| `{"status":"pending","interval":3}` | `200` | User hasn't authorized yet. Keep polling. |
| `{"status":"authorized","api_key":"fiq_live_…"}` | `200` | Success. **Plaintext is returned exactly once** — save it immediately. |
| `{"status":"expired"}` | `410` | 10-minute window elapsed. Start over from step 1. |
| `{"status":"consumed"}` | `410` | Key was already returned on a prior poll. Start over. |

The plaintext key is returned on a single successful poll and then cleared from the server. If your tool doesn't persist it on that first read, you'll need to start a new grant.

### 4. Use the key

Store the key somewhere your tool can read it on future runs — typically an environment variable:

```bash
export FINGERPRINTIQ_API_KEY='fiq_live_…'
```

Don't write the key to a shared or world-readable file without warning the user. The safest default is to export it for the current shell session and let the user add it to their `~/.zshrc`, `~/.bashrc`, or project `.env` themselves.

The issued key is visible in Dashboard → API Keys under the name your tool supplied (e.g. `my-cli (CLI)`) and can be revoked there at any time.

## Rate Limits

| Plan | Requests/second | Monthly limit |
|------|-----------------|---------------|
| Free | 10 | 10,000 |
| Builder | 1,000 | 50,000 |
| Growth | 1,000 | 250,000 |
| Scale | 1,000 | 2,000,000 |

When rate limited, the API returns `429 Too Many Requests` with a JSON body describing the limit:

```json
{
  "error": "Monthly API call limit exceeded",
  "usage": 10001,
  "limit": 10000
}
```

The per-second rate limit applies to burst traffic. If your application generates identification requests in bursts (e.g., page load events), implement exponential backoff on 429 responses.

## Security Best Practices

Never expose your API key in client-side JavaScript, public repositories, or build artifacts. Use environment variables on your server and `NEXT_PUBLIC_` / Vite `import.meta.env.VITE_` prefixes only for public keys used directly in the browser SDK.

- **Server-side keys** — Use for calling the visits API (`/v1/demo/visits/:id`). These keys must never reach the browser.
- **Client-side keys** — The SDK uses your key to call `/v1/identify`. This endpoint enforces domain allowlisting, so even if the key is visible in source, it cannot be used from unauthorized domains.
- **Key rotation** — Rotate keys periodically (every 90 days) and immediately if you suspect exposure.