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

# Authentication

> API keys and the callback-domain allowlist for the Partner API.

## API keys

Create Partner API keys in the admin portal under **API Keys**. Send the key
as a bearer token on every request:

```bash theme={null}
curl https://api-sg.quippy-lab.com/api/partner/v1/tests \
  -H "Authorization: Bearer qk_live_..."
```

Keys come in two modes, distinguished by prefix:

| Prefix      | Mode |
| ----------- | ---- |
| `qk_live_…` | Live |
| `qk_test_…` | Test |

Both are fully functional; the prefix is only so your side can tell
environments apart. Every key is scoped to your institution — every test you
list and every assignment you create is automatically scoped to it.

<Warning>
  The full key is shown **only once**, when you create it. Store it in your
  server-side secret store immediately. If you lose it, revoke it and create a
  new one — the original cannot be retrieved.
</Warning>

<Note>
  **Server-to-server only.** Never put an API key in browser JavaScript, a
  mobile app, or any client your users can inspect. Rotate immediately if a
  key is exposed — you can run two active keys at once during a rotation.
</Note>

## Callback domains

Before you can assign a test, allowlist the domain(s) your students return to
after finishing. In the admin portal, under **API Keys → Callback domains**,
add either:

* an exact hostname — `portal.example.com`, or
* a leading-dot wildcard — `.example.com`, which matches the domain and any
  subdomain.

At assignment time, the `callbackUrl` you provide must be **HTTPS** and its
host must match an allowlisted entry, or the request is rejected with
`403 forbidden`. This protects both sides against open-redirect abuse.

## Errors

Every error response uses the same envelope:

```json theme={null}
{
  "error": {
    "code": "invalid_api_key",
    "message": "Unknown or revoked API key",
    "requestId": "req_9f2c..."
  }
}
```

Include the `requestId` when contacting support — it points straight at the
failed request.

| HTTP | `code`                 | When                                                             |
| ---- | ---------------------- | ---------------------------------------------------------------- |
| 400  | `validation_error`     | Malformed body, missing required field, `metadata` over 1 KB     |
| 401  | `invalid_api_key`      | Missing, malformed, unknown, or revoked key                      |
| 403  | `forbidden`            | Test not published to you, or `callbackUrl` host not allowlisted |
| 404  | `not_found`            | Unknown `testId` / `assignmentId`                                |
| 409  | `idempotency_conflict` | An `Idempotency-Key` was reused with a different body            |
| 5xx  | `internal_error`       | Transient — retry with backoff                                   |
