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

# Authentication

> Learn how to authenticate with the Quiz Quail API using API keys and scopes.

## API Keys

Every request to the Quiz Quail API must be authenticated with an API key. You can create and manage keys from your [dashboard settings](https://app.quiz-quail.com/settings/api-keys).

### Key Format

API keys use the prefix `qk_live_` followed by 40 hex characters:

```
qk_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```

When you create a key, the full value is shown **once**. After that, only the first 16 characters (the prefix) are stored for identification. Copy and store your key securely before closing the dialog.

### Creating a Key

1. Go to **Settings > API Keys** in the Quiz Quail dashboard.
2. Click **Create API Key**.
3. Give the key a descriptive name (e.g., "CI Pipeline" or "Mobile App").
4. Select the [scopes](#scopes) your key needs.
5. Copy the key immediately and store it in a secure location.

### Revoking a Key

You can revoke a key at any time from the API Keys settings page. Revoked keys are rejected immediately on the next request. Revocation cannot be undone — you will need to create a new key.

***

## Using Your API Key

Pass your API key in the `Authorization` header as a Bearer token.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET https://app.quiz-quail.com/api/v1/quizzes \
    -H "Authorization: Bearer qk_live_YOUR_API_KEY"
  ```

  ```javascript JavaScript (fetch) theme={null}
  const response = await fetch("https://app.quiz-quail.com/api/v1/quizzes", {
    headers: {
      Authorization: "Bearer qk_live_YOUR_API_KEY",
    },
  });

  const data = await response.json();
  ```

  ```python Python (requests) theme={null}
  import requests

  response = requests.get(
      "https://app.quiz-quail.com/api/v1/quizzes",
      headers={"Authorization": "Bearer qk_live_YOUR_API_KEY"},
  )

  data = response.json()
  ```
</CodeGroup>

<Warning>
  Never include your API key directly in client-side code, URLs, or query parameters. Always send it in the `Authorization` header over HTTPS.
</Warning>

***

## Scopes

Each API key is assigned one or more **scopes** that control which endpoints it can access. Follow the principle of least privilege — only grant the scopes your integration actually needs.

### Available Scopes

| Scope            | Description                                 |
| ---------------- | ------------------------------------------- |
| `quizzes:read`   | Read quizzes, rounds, and questions         |
| `quizzes:write`  | Create, update, and delete quizzes          |
| `themes:read`    | Read themes                                 |
| `themes:write`   | Create, update, and delete themes           |
| `media:read`     | Read and list media files                   |
| `media:write`    | Upload and delete media files               |
| `renders:read`   | Read render jobs and their status           |
| `renders:write`  | Create and manage render jobs               |
| `youtube:read`   | Read YouTube upload status and metadata     |
| `youtube:write`  | Upload videos to YouTube and manage uploads |
| `webhooks:read`  | Read webhook configurations                 |
| `webhooks:write` | Create, update, and delete webhooks         |
| `ai:read`        | Read AI generation history and status       |
| `ai:write`       | Generate quizzes and content using AI       |

### Common Scope Combinations

| Use Case                 | Recommended Scopes                                                                          |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| Read-only dashboard      | `quizzes:read`, `themes:read`, `renders:read`                                               |
| CI/CD rendering pipeline | `quizzes:read`, `renders:read`, `renders:write`                                             |
| Full quiz management     | `quizzes:read`, `quizzes:write`, `themes:read`, `themes:write`, `media:read`, `media:write` |
| YouTube publishing       | `renders:read`, `youtube:read`, `youtube:write`                                             |
| AI-powered quiz creation | `quizzes:read`, `quizzes:write`, `ai:read`, `ai:write`                                      |

***

## Error Responses

Authentication and authorization errors follow the [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) Problem Details format.

### 401 Unauthorized

Returned when no valid credentials are provided — the key is missing, malformed, or revoked.

```json theme={null}
{
  "type": "https://quizquail.com/problems/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required. Provide an API key via Authorization: Bearer header, or use a valid session cookie."
}
```

**Common causes:**

* Missing `Authorization` header
* Incorrect key (typo or wrong environment)
* Key has been revoked

### 403 Forbidden

Returned when your key is valid but does not have the required scopes for the endpoint.

```json theme={null}
{
  "type": "https://quizquail.com/problems/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "Insufficient scopes. Required: quizzes:write. Your key has: quizzes:read"
}
```

**How to fix:** Create a new API key with the required scopes, or update your existing key's scope configuration. The error `detail` field tells you exactly which scopes are missing versus which scopes your key has.

***

## Security Best Practices

<AccordionGroup>
  <Accordion title="Store keys in environment variables">
    Never hard-code API keys in source code. Use environment variables or a secrets manager.

    ```bash theme={null}
    # .env (add to .gitignore!)
    QUIZ_QUAIL_API_KEY=qk_live_YOUR_API_KEY
    ```

    ```javascript theme={null}
    const response = await fetch("https://app.quiz-quail.com/api/v1/quizzes", {
      headers: {
        Authorization: `Bearer ${process.env.QUIZ_QUAIL_API_KEY}`,
      },
    });
    ```
  </Accordion>

  <Accordion title="Use minimum required scopes">
    Grant each key only the scopes it needs. A rendering pipeline does not need `quizzes:write`. A read-only dashboard does not need `media:write`. If a key is compromised, limited scopes reduce the blast radius.
  </Accordion>

  <Accordion title="Rotate keys regularly">
    Create a new key, update your integration to use it, then revoke the old key. This limits the window of exposure if a key is leaked.
  </Accordion>

  <Accordion title="Use separate keys per environment">
    Create distinct keys for development, staging, and production. If a development key is exposed, your production data is unaffected.
  </Accordion>

  <Accordion title="Never expose keys in client-side code">
    API keys should only be used in server-side code. Never embed them in JavaScript bundles, mobile apps, or browser requests where they can be inspected.
  </Accordion>

  <Accordion title="Monitor key usage">
    Check the **Last Used** column in your API Keys settings to identify unused keys. Revoke any key that is no longer in active use.
  </Accordion>
</AccordionGroup>
