# Kourier auth.md

You are an agent. Kourier (https://kourier.sh) serves DeepSeek V4 Flash through OpenAI- and Anthropic-compatible APIs on flat monthly plans. This document tells you how to obtain a Kourier API key for your user: discover → register → have the user confirm a code → poll for the key → call the API.

Kourier supports one registration method, `service_auth`: you supply the user's email and the user confirms in their browser. Anonymous and ID-JAG registration are not supported, because API access requires a signed-in account with a Pro, Max, or Omega plan.

## Step 1 — Discover (optional)

A request to the API without a key returns `401` with:

```http
WWW-Authenticate: Bearer resource_metadata="https://api.kourier.sh/.well-known/oauth-protected-resource"
```

That document lists the authorization server, `https://app.kourier.sh`. Its metadata, including the `agent_auth` block, is at `https://app.kourier.sh/.well-known/oauth-authorization-server`. The endpoints below are the ones it lists.

## Step 2 — Confirm with the user

Tell the user you want to create a Kourier API key for their account, which email you will use, and that the key will be able to use their plan until they revoke it. Continue only if they agree.

## Step 3 — Register

```http
POST https://app.kourier.sh/agent/identity
Content-Type: application/json

{ "type": "service_auth", "login_hint": "user@example.com", "client_name": "Your agent name" }
```

`client_name` is optional and is shown to the user on the confirmation page. Response:

```json
{
  "registration_id": "reg_...",
  "registration_type": "service_auth",
  "claim_url": "https://app.kourier.sh/agent/identity/claim",
  "claim_token": "clm_...",
  "claim_token_expires": "2026-09-24T13:00:00.000Z",
  "post_claim_scopes": ["inference"],
  "claim": {
    "user_code": "123456",
    "expires_in": 600,
    "verification_uri": "https://app.kourier.sh/claim?claim_attempt_token=cla_...",
    "interval": 5
  }
}
```

Keep `claim_token` in memory only; it is returned once.

## Step 4 — Hand off to the user

Show the user `claim.verification_uri` and `claim.user_code`. They open the link, sign in to Kourier with the same email, type the code, and approve. If they have no plan yet, the page asks them to choose Pro or Max first.

## Step 5 — Poll for the key

```http
POST https://app.kourier.sh/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=clm_...
```

Poll no faster than every `interval` seconds.

| Response | Meaning | What to do |
| --- | --- | --- |
| `authorization_pending` | The user hasn't confirmed yet | Keep polling |
| `slow_down` | You polled too fast | Wait longer between polls |
| `expired_token` (code) | The 10-minute code expired | `POST https://app.kourier.sh/agent/identity/claim` with `{ "claim_token", "email" }` for a new code, then repeat Step 4 |
| `expired_token` (registration) | The 1-hour registration expired | Start again at Step 3 |
| `access_denied` | The user denied, or the key couldn't be created (see `error_description`) | Tell the user |
| `200` | Success | See below |

Success:

```json
{ "access_token": "sk-bf-...", "token_type": "Bearer", "scope": "inference" }
```

The `access_token` is a normal Kourier API key. It does not expire. Store it where your tool reads credentials (for example the `KOURIER_API_KEY` environment variable), never in a repository.

## Step 6 — Call the API

- OpenAI-compatible: `https://api.kourier.sh/v1`, header `Authorization: Bearer <key>`
- Anthropic-compatible: `https://api.kourier.sh/anthropic`, header `Authorization: Bearer <key>` or `x-api-key: <key>`
- Model: `DSV4-Flash-0731`

`429` with `Retry-After` means the plan's concurrency limit (Pro 3, Max 7 simultaneous requests) is reached. For per-tool configuration, read https://kourier.sh/.well-known/agent-skills/kourier-setup/SKILL.md.

## Alternative: browser sign-in (OAuth 2.0 + PKCE)

If you can open a browser and listen on a loopback port, use the authorization code flow instead of Steps 3–5: send the user to `https://app.kourier.sh/oauth2/authorize` with `response_type=code`, `client_id`, a loopback `redirect_uri` (`http://127.0.0.1:<port>/callback`), `code_challenge` + `code_challenge_method=S256`, and `state`; then exchange the returned `code` at `https://app.kourier.sh/oauth2/token` with `grant_type=authorization_code`, `client_id`, `redirect_uri`, and `code_verifier`. The `access_token` is the same kind of API key.

## Revocation

Revoke a key you no longer need:

```http
POST https://app.kourier.sh/oauth2/revoke
Content-Type: application/x-www-form-urlencoded

token=sk-bf-...
```

The user can also revoke any key under Dashboard → API Keys. A revoked key returns `401`.

## References

- Authorization server metadata: https://app.kourier.sh/.well-known/oauth-authorization-server
- API catalog: https://kourier.sh/.well-known/api-catalog
- OpenAPI description: https://kourier.sh/openapi.json
- Docs: https://docs.kourier.sh/docs
