# Authenticatie

Elke aanroep die een model uitvoert of je requests leest, heeft een API-sleutel nodig. De catalogus (`GET /models`,
`GET /categories`), `POST /quote` en het OpenAPI-document zijn openbaar.

## API-sleutels

Maak sleutels aan en trek ze in via [https://doitong.com/developers/dashboard#keys](https://doitong.com/developers/dashboard#keys). Een sleutel ziet eruit als `doitong_` gevolgd door 64 hexadecimale tekens en wordt alleen weergegeven wanneer je hem aanmaakt. Verzend hem in de
`Authorization`-header:

```bash
curl https://api.doitong.com/v1/account -H "Authorization: Bearer $DOITONG_API_KEY"
```

`X-API-Key: <key>` werkt ook.

> [!WARNING]
> Bewaar sleutels op je server: iedereen met een sleutel kan de API-credits uitgeven waartoe die sleutel gemachtigd is. Als een sleutel uitlekt, trek hem dan in en maak een nieuwe aan.

Een request zonder sleutel krijgt `401` met code `missing_api_key`; een onbekende, ingetrokken of verlopen sleutel krijgt ook `401`.

## Scopes

Elke sleutel bevat scopes. De model-API controleert deze:

| Actie | Vereiste scope |
|---|---|
| Een model uitvoeren, een request annuleren, de bestanden van een request verwijderen, de opslaginstelling wijzigen | `models:run` (sleutels met het oudere `images:create` of `videos:create` werken ook) |
| Een upload-URL aanmaken | `models:run` of `media:upload` |
| Requests en account lezen | `models:read` (een sleutel waarmee modellen mogen worden uitgevoerd kan ook lezen) |

Nieuwe sleutels krijgen standaard `models:read` en `models:run`. Een aanroep zonder de juiste scope krijgt `403` met code
`insufficient_scope`.

## Creditbudgetten

Een sleutel kan een eigen budget in API-credits hebben (1.000 API-tegoed = $ 1), ingesteld door de accounteigenaar: een totaalbudget, een dagbudget (laatste 24 uur), of beide. Voordat een run in rekening wordt gebracht, wordt de kostprijs vergeleken met wat de sleutel al via deze API heeft uitgegeven (na aftrek van terugbetalingen). Een run die het budget zou overschrijden wordt geweigerd met `402 budget_exceeded` en er worden geen kosten in rekening gebracht.

> [!IMPORTANT]
> Een budgetweigering is definitief: opnieuw proberen helpt niet totdat de eigenaar het budget verhoogt.

Een sleutel zonder budget kan tot het API-saldo uitgeven. Los daarvan krijgt een run die niet door het API-saldo kan worden gedekt `402 insufficient_credits` met een `top_up_url`.

`GET https://api.doitong.com/v1/account` toont het API-saldo en het resterende budget van de sleutel (beide in API-credits):

```json 200 OK · account
{
  "object": "account",
  "credits": 125000,
  "balance": { "credits": 125000, "usd": "125.00" },
  "top_up_url": "https://doitong.com/developers/dashboard#balance",
  "key": {
    "name": "Production server",
    "prefix": "doitong_3fa9c1e...",
    "scopes": ["models:read", "models:run"],
    "budget_remaining": 50000,
    "rate_limit": 3600,
    "rate_limit_remaining": 3571
  },
  "webhook_signing_secret": "whsec_..."
}
```

`budget_remaining` is `null` wanneer de sleutel geen eigen budget heeft.

## Snelheidslimieten

Elke sleutel mag 3600 requests per uur uitvoeren, tenzij de limiet anders is ingesteld. Elke geauthenticeerde response bevat de huidige status:

| Header | Betekenis |
|---|---|
| `X-RateLimit-Limit` | Toegestane requests in het huidige venster |
| `X-RateLimit-Remaining` | Resterende requests in het huidige venster |
| `X-RateLimit-Reset` | Unix-tijdstip waarop het venster wordt gereset |

Bij overschrijding van de limiet krijg je `429` met een `Retry-After`-header (seconden). Long polling met `?wait=` telt als één request per aanroep, dus wachten is goedkoper dan snel pollen.

Er is ook een limiet van 10 requests die per account tegelijk worden uitgevoerd (`queued` of `running`). Een elfde request retourneert `429 too_many_inflight`; start het opnieuw nadat er een is voltooid.
