# Requests

Een request is één aanroep van één model. Het heeft een id (`req_…`), een status, de kosten in API-credits (1000 = 1 USD) en, wanneer het
slaagt, de gegenereerde bestanden. Hetzelfde object komt terug van elk endpoint hieronder, voor elk model.

## Een model vinden

De catalogus met alle 1.496 modellen is openbare JSON (geen sleutel nodig):

```bash
curl "https://api.doitong.com/v1/models?category=text-to-video&lab=Google&limit=20"
```

| Queryparameter | Betekenis |
|---|---|
| `q` | Woorden die allemaal in de naam, id, maker of tags moeten voorkomen, bijvoorbeeld `kling 3`. |
| `category` | Eén categorie-id, bijvoorbeeld `text-to-video` of `upscale`. `GET https://api.doitong.com/v1/categories` geeft alle 21 weer met hun aantallen. |
| `lab` | De maker van het model, op naam of id, bijvoorbeeld `Google` of `black-forest-labs`. |
| `family` | Een `family`-id uit een vorige rij: elke variant van één model (pro, fast, image-to-video, …). |
| `mode` | `exact` (de prijs wordt berekend op basis van de invoer), `measured` (op basis van de lengte of grootte van de invoerbestanden), `estimate` (een geschatte prijs per uitvoering) of `metered` (in rekening gebracht op basis van het werkelijke gebruik, tot een maximum per uitvoering). |
| `limit`, `cursor` | Paginagrootte (1 tot 100, standaard 50) en de `next_cursor` van de vorige pagina. |

Elke rij heeft `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` en een `price`-
object (zie [Pricing](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` voegt het
volledige invoer- en output-JSON Schema, de canonieke parameternamen, de prijsregels en een invoervoorbeeld toe. Een onbekende
`category` of `mode` is `400 invalid_parameter`.

## Een model uitvoeren

```bash
curl -X POST "https://api.doitong.com/v1/run/{model_id}?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1842-cover" \
  -d '{"prompt": "..."}'
```

- De body is de modelinvoer, een JSON-object. Elke modelpagina vermeldt de parameters.
- De invoer wordt gecontroleerd voordat er iets in rekening wordt gebracht. Ongeldige invoer geeft `422 invalid_input` met de betreffende
  `param` en kost niets.
- `?wait=N` (maximaal 20 seconden) houdt de aanroep vast totdat de request is voltooid of de tijd om is.
- `?webhook_url=https://…` verstuurt een ondertekende POST naar die URL wanneer de request is voltooid (zie [Webhooks](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` of `?storage=temporary` bepaalt hoelang de uitvoerbestanden worden bewaard; zonder deze parameter wordt de accountinstelling
  toegepast (zie [Waar bestanden worden bewaard](#storage)).

De response is `202 Accepted` zolang de request nog wordt verwerkt en `200 OK` wanneer deze binnen de wachttijd is voltooid (of
wanneer het om een replay gaat, zie hieronder). De headers `Location` en `Doitong-Request-Id` bevatten de URL en id van de request.

## Statussen

Levenscyclus van een request: `queued` → `running` → `succeeded` | `failed` | `canceled` (definitief; mislukte en geannuleerde aanvragen worden terugbetaald).

| Status | Betekenis | Definitief |
|---|---|---|
| `queued` | Geaccepteerd en in rekening gebracht, wachtend op een worker. `queue_position` kan de plaats in de wachtrij tonen. | nee |
| `running` | Het model genereert. | nee |
| `succeeded` | Voltooid. `outputs` bevat de bestanden. | ja |
| `failed` | Het model of een limiet heeft de request gestopt. `error` geeft de reden. De API-credits worden terugbetaald. | ja |
| `canceled` | Geannuleerd voordat er een resultaat werd geproduceerd. De API-credits worden terugbetaald. | ja |

Een request die langer loopt dan de tijdslimiet mislukt met code `timeout` en wordt terugbetaald: 15 minuten voor afbeeldingen,
60 minuten voor video, 30 minuten voor audio en al het overige.

## Het request-object

```json
{
  "id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
  "object": "request",
  "model": "flux/schnell",
  "status": "succeeded",
  "created_at": "2026-10-20T12:00:00Z",
  "started_at": "2026-10-20T12:00:01Z",
  "completed_at": "2026-10-20T12:00:02Z",
  "queue_position": null,
  "cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
  "input": { "prompt": "..." },
  "output": { "images": [ { "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "width": 1024, "height": 768 } ] },
  "outputs": [
    { "type": "image", "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "content_type": "image/jpeg",
      "width": 1024, "height": 768, "file_size": 183920, "stored": true }
  ],
  "error": null,
  "metrics": { "inference_seconds": 1.2 },
  "storage": { "mode": "permanent", "bytes": 183920, "expires_at": null, "expired": false },
  "urls": {
    "self": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
    "cancel": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
  }
}
```

- `outputs` is voor elk model hetzelfde: één item per gegenereerd bestand met `type` (`image`, `video`, `audio`,
  `file` of `3d`), een `url` in onze opslag, `content_type` en, wanneer bekend, `file_size`, `width`, `height`,
  `duration`.
- `output` is het eigen resultaatobject van het model (de velden ervan staan op elke modelpagina), met dezelfde URL's.
- `storage` geeft aan hoe de bestanden worden bewaard: `mode` (`permanent` of `temporary`), opgeslagen `bytes`, `expires_at` (wanneer een
  tijdelijk bestand wordt verwijderd, `null` voor permanente bestanden) en `expired` (`true` zodra de bestanden verdwenen zijn).
- `cost.credits` is de prijs van de aanroep in API-credits (`cost.usd` in USD), `cost.charged` wat van het
  API-saldo is afgeschreven en `cost.refunded` wat is teruggekomen. Voor een `metered`-model is `cost.credits` het maximum dat aan het
  begin is vastgehouden (`cost.held`); zodra de uitvoering slaagt, geeft `cost.settled` aan wat het gebruik heeft gekost en staat de rest van het maximum
  in `cost.refunded` (zie [In rekening gebracht op basis van gebruik](https://doitong.com/developers/docs/pricing#metered)).
- Wanneer een request mislukt, is `error` `{ "type": …, "code": …, "message": … }`.

## Een request lezen

```bash
curl "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- `?wait=N` (maximaal 20 seconden) voert long-polling uit: de aanroep retourneert zodra de request definitief is, of na N seconden met
  de huidige status. Dit herhalen is de eenvoudigste manier om op een resultaat te wachten.
- `?logs=1` voegt de voortgangsregels van het model (de laatste 50) toe als `logs`.

Je kunt alleen requests van je eigen account lezen; elke andere id is `404 request_not_found`.

## Requests weergeven

```bash
curl "https://api.doitong.com/v1/requests?limit=20&status=succeeded&model=flux/schnell" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Nieuwste eerst. `limit` is 1 tot 100 (standaard 20), `status` en `model` filteren. De response is
`{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; geef `cursor=<next_cursor>` mee voor
de volgende pagina.

## Een request annuleren

```bash
curl -X POST "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- Geannuleerd voordat het model startte: de status wordt `canceled`, de API-credits worden terugbetaald en je krijgt `200`.
- Al bezig met genereren: de annulering wordt doorgegeven en je krijgt `202` met `"cancel_requested": true`. Als het model
  toch voltooit, slaagt de request normaal en wordt deze in rekening gebracht.
- Al definitief: `409 not_cancelable` (een request die al `canceled` is, retourneert gewoon `200`).

## De bestanden van een request verwijderen

```bash
curl -X DELETE "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/files" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Verwijdert nu onze kopieën van de uitvoerbestanden van de request en stopt de opslagkosten. De response is het request-
object met `storage.expired: true`; de URL's blijven in het record staan, maar werken niet meer. Nogmaals aanroepen verandert niets
(`200`). Een request die nog `queued` of `running` is, heeft nog geen bestanden: `409 request_active`. Vereist `models:run`.

## Idempotentie: opnieuw proberen zonder twee keer te betalen

Netwerken kunnen uitvallen. Stuur om een uitvoering veilig opnieuw te proberen een `Idempotency-Key`-header mee (een willekeurige tekenreeks van maximaal 255 tekens, uniek per
bewerking, bijvoorbeeld je eigen order-id):

- Dezelfde sleutel en dezelfde body als eerder: je krijgt de oorspronkelijke request terug (`200`, header
  `Idempotent-Replayed: true`) en er wordt niets opnieuw in rekening gebracht.
- Dezelfde sleutel en body na een uitvoering die mislukte voordat deze het model bereikte (`upstream_error` of `internal_error` bij
  submit, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): er is niets geproduceerd en er is niets
  in rekening gebracht, dus de retry voert het model opnieuw uit in plaats van de fout terug te geven.
- Dezelfde sleutel met een andere body: `409 idempotency_mismatch`.
- Sleutels zijn gekoppeld aan de API-sleutel die ze heeft verzonden.

## Waar bestanden worden bewaard

Elk uitvoerbestand wordt naar onze opslag gekopieerd voordat de request als `succeeded` wordt gemarkeerd, dus de URL's in `outputs` staan op
onze eigen hosts en je kunt er rechtstreeks naar linken. Hoelang ze blijven staan, bepaal je zelf:

| Modus | Hoelang | Prijs |
|---|---|---|
| `permanent` (standaard) | Totdat je ze verwijdert | 100 API-credits ($ 0,10) per GB per maand, dagelijks afgeschreven van het API-saldo |
| `temporary` | 7 dagen, daarna verwijderd | Gratis |

- De accountinstelling bepaalt dit voor nieuwe requests: de schakelaar "Bestanden voor altijd bewaren" in het
  [dashboard](https://doitong.com/developers/dashboard#balance), of `PATCH https://api.doitong.com/v1/account/storage` met
  `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` toont deze onder `storage`, met de bytes die je bewaart en de
  maandelijkse schatting.
- `?storage=permanent|temporary` bij een uitvoering (het `storage`-argument van de MCP-tool `run`) overschrijft deze instelling voor die uitvoering.
- Het wijzigen van de instelling heeft nooit invloed op bestanden die je al hebt; verwijder die met
  [`DELETE /requests/{id}/files`](#delete-files).
- Wanneer het API-saldo de opslag niet kan betalen, wordt er niets in rekening gebracht en krijg je een e-mail: waardeer binnen
  30 dagen op, anders worden de bewaarde bestanden tijdelijk en 7 dagen later verwijderd.

In het zeldzame geval dat onze kopie mislukt, bevat het item `"stored": false` en is de URL de oorspronkelijke, kort geldige URL:
download het bestand snel.
