# Quiz.Video auth.md

You are an agent. This document tells you how to get credentials for the Quiz.Video API (`https://www.quiz.video/api/v1`) and MCP server (`https://www.quiz.video/mcp/`). Follow the steps in order.

Quiz.Video issues credentials in two ways, and both are tied to a human account that approved them:

- **OAuth 2.1** with dynamic client registration, authorization code + PKCE, and refresh tokens.
- **API keys** that the user creates in their account settings.

Quiz.Video does not offer the auth.md agentic-registration endpoints (`/agent/identity`, the claim ceremony, or ID-JAG / JWT-bearer exchange). There is no anonymous or email-only self-registration for agents.

## Step 1 — Discover

An unauthenticated call to a protected `/api/v1` endpoint returns `401` with a pointer to the protected resource metadata (`GET /api/v1/` itself is public):

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://www.quiz.video/.well-known/oauth-protected-resource/api/v1", scope="api:read"
```

On the MCP server, an unauthenticated `tools/call` for a `quiz_video_*` tool returns `401` pointing at `https://www.quiz.video/.well-known/oauth-protected-resource/mcp`; `initialize`, `tools/list` and the discovery tools work without credentials.

Fetch the Protected Resource Metadata ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728)):

- REST API: `GET https://www.quiz.video/.well-known/oauth-protected-resource/api/v1` (resource `https://www.quiz.video/api/v1`)
- MCP server: `GET https://www.quiz.video/.well-known/oauth-protected-resource/mcp` (resource `https://www.quiz.video/mcp`)
- Whole site: `GET https://www.quiz.video/.well-known/oauth-protected-resource` (resource `https://www.quiz.video`)

Each lists `authorization_servers: ["https://www.quiz.video"]`, `scopes_supported: ["api:read", "api:write"]`, and `bearer_methods_supported: ["header"]`.

Then fetch the Authorization Server metadata ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)):

```http
GET https://www.quiz.video/.well-known/oauth-authorization-server
```

Use its `registration_endpoint`, `authorization_endpoint`, `token_endpoint`, and `revocation_endpoint`. `code_challenge_methods_supported` is `["S256"]`.

## Step 2 — Pick a method

1. **You can send the user to a browser, or you are an MCP client** → OAuth (Step 3).
2. **The user can paste a secret into your configuration** → API key (Step 4).
3. **Neither** → ask the user to create a free account at https://www.quiz.video/auth/sign-up/ (no card required), then use method 1 or 2.

## Step 3 — OAuth authorization code + PKCE

### 3a. Register a client

```http
POST https://www.quiz.video/api/auth/oauth2/register
Content-Type: application/json

{
  "client_name": "Your Agent",
  "redirect_uris": ["https://your-agent.example.com/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
```

Keep the returned `client_id`. Registration does not require authentication.

### 3b. Send the user to authorize

```http
GET https://www.quiz.video/api/auth/oauth2/authorize?response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyour-agent.example.com%2Fcallback
  &scope=openid%20offline_access%20api%3Aread%20api%3Awrite
  &code_challenge=PKCE_CHALLENGE&code_challenge_method=S256
  &state=RANDOM_STATE
  &resource=https%3A%2F%2Fwww.quiz.video%2Fapi%2Fv1
```

The user signs in (or signs up) and approves the requested scopes on the consent screen. Ask only for the scopes you need: `api:read` covers GET requests, `api:write` covers everything that creates, changes, or deletes.

### 3c. Exchange the code

```http
POST https://www.quiz.video/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&code_verifier=PKCE_VERIFIER&resource=https%3A%2F%2Fwww.quiz.video%2Fapi%2Fv1
```

Always send `resource`: it makes the server issue a JWT access token audienced for the API. Opaque tokens (prefix `qv_at_`) are rejected by `/api/v1`. Tokens audienced for `https://www.quiz.video/mcp` or `https://www.quiz.video` are accepted too.

When the access token expires, use the refresh token:

```http
POST https://www.quiz.video/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=CLIENT_ID&resource=https%3A%2F%2Fwww.quiz.video%2Fapi%2Fv1
```

## Step 4 — API key

The user creates a key at https://www.quiz.video/app/settings/ (API Keys tab). Keys start with `qv_` and are available on every plan, including free. Send it on each request:

```http
GET https://www.quiz.video/api/v1/quizzes
x-api-key: qv_...
```

`Authorization: Bearer qv_...` also works. Each key has a budget of 1,000 requests, which refills once the key has made no requests for 24 hours.

## Step 5 — Call the API

```http
GET https://www.quiz.video/api/v1/account
Authorization: Bearer ACCESS_TOKEN
```

Start from `GET https://www.quiz.video/api/v1/` (no auth needed) for the list of resources, or read the OpenAPI spec at `https://www.quiz.video/.well-known/openapi.json`.

## Errors

Errors from `/api/v1` are JSON: `{ "error": "...", "code": "...", "hint": "..." }`. Errors from the token endpoint use OAuth vocabulary ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2)).

| Code | Where | What to do |
| --- | --- | --- |
| `UNAUTHORIZED` (401) | `/api/v1` | Credentials missing, expired, or invalid. Refresh the access token once; if that fails, restart at Step 3 or ask the user for a new API key. |
| `INSUFFICIENT_SCOPE` (403) | `/api/v1` | The token lacks the scope named in `WWW-Authenticate`. Re-authorize with that scope. |
| `RATE_LIMITED` (429) | `/api/v1` | Wait `Retry-After` seconds, then retry. |
| `FREE_PLAN_LIMIT_REACHED` (403) | quiz creation | The free plan's monthly quiz is used. The user can upgrade at https://www.quiz.video/pricing/. |
| `invalid_grant` | token endpoint | The code or refresh token is expired or revoked. Restart at Step 3b. |
| `invalid_client` | token endpoint | Unknown `client_id`. Register again (Step 3a). |

Retry 5xx responses with exponential backoff. Do not retry 4xx responses with the same payload.

## Revocation

- Users can delete API keys at any time in https://www.quiz.video/app/settings/. A deleted key returns `401`.
- Clients registered with a client secret can revoke OAuth tokens at the `revocation_endpoint` from the authorization server metadata ([RFC 7009](https://www.rfc-editor.org/rfc/rfc7009)). Public clients (`token_endpoint_auth_method: "none"`) cannot authenticate there; discard tokens you no longer need, and access tokens expire on their own.

Full API reference: https://www.quiz.video/docs/api/ · Developer portal: https://www.quiz.video/developers/
