# Anmodninger

En anmodning er ét kald til én model. Den har et id (`req_…`), en status, sin pris i API-kreditter (1000 = 1 USD) og, når den
lykkes, de genererede filer. Det samme objekt returneres fra hvert endpoint nedenfor for hver model.

## Find en model

Kataloget med alle 1.496 modeller er offentlig JSON (ingen nøgle nødvendig):

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

| Forespørgselsparameter | Betydning |
|---|---|
| `q` | Ord, der alle skal forekomme i navnet, id'et, producenten eller tags, f.eks. `kling 3`. |
| `category` | Ét kategori-id, f.eks. `text-to-video` eller `upscale`. `GET https://api.doitong.com/v1/categories` viser alle 21 med deres antal. |
| `lab` | Modellens producent, efter navn eller id, f.eks. `Google` eller `black-forest-labs`. |
| `family` | Et `family`-id fra en tidligere række: hver variant af én model (pro, fast, image-to-video, …). |
| `mode` | `exact` (prisen beregnes ud fra input), `measured` (ud fra længden eller størrelsen på inputfilerne), `estimate` (en estimeret pris pr. kørsel) eller `metered` (opkræves efter faktisk forbrug, op til et maksimum pr. kørsel). |
| `limit`, `cursor` | Sidestørrelse (1 til 100, standard 50) og `next_cursor` fra den forrige side. |

Hver række har `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` og et `price`
objekt (se [Priser](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` tilføjer det
fulde input- og output-JSON Schema, de kanoniske parameternavne, prisreglerne og et eksempel på input. En ukendt
`category` eller `mode` er `400 invalid_parameter`.

## Kør en model

```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": "..."}'
```

- Brødteksten er modellens input, et JSON-objekt. Hver modelside viser dens parametre.
- Input valideres, før der opkræves noget. Ugyldigt input returnerer `422 invalid_input` med den fejlbehæftede
  `param` og koster ingenting.
- `?wait=N` (op til 20 sekunder) holder kaldet tilbage, indtil anmodningen er færdig, eller tiden udløber.
- `?webhook_url=https://…` sender en signeret POST til URL'en, når anmodningen er færdig (se [Webhooks](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` eller `?storage=temporary` vælger, hvor længe outputfilerne opbevares; uden denne parameter
  anvendes kontoindstillingen (se [Hvor filer opbevares](#storage)).

Svaret er `202 Accepted`, mens anmodningen stadig behandles, og `200 OK`, når den blev færdig inden for ventetiden (eller
når det er en gentagelse, se nedenfor). Headerne `Location` og `Doitong-Request-Id` indeholder anmodningens URL og id.

## Statusser

Requestens livscyklus: `queued` → `running` → `succeeded` | `failed` | `canceled` (afsluttet; mislykkede og annullerede refunderes).

| Status | Betydning | Endelig |
|---|---|---|
| `queued` | Accepteret og opkrævet, venter på en worker. `queue_position` kan vise pladsen i køen. | nej |
| `running` | Modellen genererer. | nej |
| `succeeded` | Færdig. `outputs` viser filerne. | ja |
| `failed` | Modellen eller en grænse stoppede den. `error` angiver hvorfor. API-kreditterne tilbagebetales. | ja |
| `canceled` | Annulleret, før den producerede et resultat. API-kreditterne tilbagebetales. | ja |

En anmodning, der kører længere end sin tidsgrænse, mislykkes med koden `timeout` og tilbagebetales: 15 minutter for billeder,
60 minutter for video, 30 minutter for lyd og alt andet.

## Anmodningsobjektet

```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` er ens for alle modeller: én post pr. genereret fil med `type` (`image`, `video`, `audio`,
  `file` eller `3d`), en `url` på vores lagring, `content_type` og, når det er kendt, `file_size`, `width`, `height`,
  `duration`.
- `output` er modellens eget resultatobjekt (dets felter står på hver modelside) med de samme URL'er.
- `storage` angiver, hvordan filerne opbevares: `mode` (`permanent` eller `temporary`), gemte `bytes`, `expires_at` (hvornår en
  midlertidig fil slettes, `null` for permanente) og `expired` (`true`, når filerne er væk).
- `cost.credits` er prisen for kaldet i API-kreditter (`cost.usd` i USD), `cost.charged` det, der blev trukket fra
  API-saldoen, og `cost.refunded` det, der blev tilbageført. For en `metered`-model er `cost.credits` det maksimale beløb, der holdes ved
  starten (`cost.held`); når kørslen lykkes, angiver `cost.settled`, hvad forbruget blev, og resten af maksimumet står i
  `cost.refunded` (se [Opkrævning efter forbrug](https://doitong.com/developers/docs/pricing#metered)).
- Når en anmodning mislykkes, er `error` `{ "type": …, "code": …, "message": … }`.

## Læs en anmodning

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

- `?wait=N` (op til 20 sekunder) foretager long polling: kaldet returnerer, så snart anmodningen er endelig, eller efter N sekunder med
  den aktuelle tilstand. At gentage dette er den enkleste måde at vente på et resultat.
- `?logs=1` tilføjer modellens fremdriftsloglinjer (de seneste 50) som `logs`.

Du kan kun læse anmodninger fra din egen konto; ethvert andet id er `404 request_not_found`.

## Vis anmodninger

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

Nyeste først. `limit` er 1 til 100 (standard 20), og `status` og `model` filtrerer. Svaret er
`{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; send `cursor=<next_cursor>` for
den næste side.

## Annuller en anmodning

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

- Annulleret, før modellen startede: status bliver `canceled`, API-kreditterne tilbagebetales, og du får `200`.
- Allerede under generering: annulleringen videresendes, og du får `202` med `"cancel_requested": true`. Hvis modellen
  alligevel bliver færdig, lykkes anmodningen normalt, og der opkræves betaling.
- Allerede endelig: `409 not_cancelable` (en anmodning, der allerede er `canceled`, returnerer blot `200`).

## Slet anmodningens filer

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

Sletter vores kopier af anmodningens outputfiler nu og stopper deres lagringsgebyr. Svaret er anmodningsobjektet med
`storage.expired: true`; URL'erne bliver i posten, men virker ikke længere. Et nyt kald ændrer intet
(`200`). En anmodning, der stadig er `queued` eller `running`, har endnu ingen filer: `409 request_active`. Kræver `models:run`.

## Idempotens: prøv igen uden at betale to gange

Netværk fejler. For sikkert at prøve en kørsel igen skal du sende en `Idempotency-Key`-header (en vilkårlig streng på højst 255 tegn, unik pr.
handling, f.eks. dit eget ordre-id):

- Samme nøgle og samme brødtekst som før: du får den oprindelige anmodning tilbage (`200`, headeren
  `Idempotent-Replayed: true`), og der opkræves ikke igen.
- Samme nøgle og brødtekst efter en kørsel, der mislykkede, før den nåede modellen (`upstream_error` eller `internal_error` ved
  indsendelse, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): intet blev produceret, og intet
  opkræves, så forsøget kører modellen igen i stedet for at returnere fejlen.
- Samme nøgle med en anden brødtekst: `409 idempotency_mismatch`.
- Nøgler er knyttet til den API-nøgle, der sendte dem.

## Hvor filer opbevares

Hver outputfil kopieres til vores lagring, før anmodningen markeres som `succeeded`, så URL'erne i `outputs` ligger på
vores egne værter, og du kan linke direkte til dem. Du vælger selv, hvor længe de bliver:

| Tilstand | Varighed | Pris |
|---|---|---|
| `permanent` (standard) | Indtil du sletter dem | 100 API-kreditter (0,10 $) pr. GB pr. måned, opkrævet dagligt fra API-saldoen |
| `temporary` | 7 dage, derefter slettet | Gratis |

- Kontoindstillingen bestemmer nye anmodninger: kontakten "Gem filer for altid" i
  [dashboardet](https://doitong.com/developers/dashboard#balance) eller `PATCH https://api.doitong.com/v1/account/storage` med
  `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` viser den under `storage` sammen med de bytes, du opbevarer, og det
  månedlige estimat.
- `?storage=permanent|temporary` på en kørsel (argumentet `storage` i MCP-værktøjet `run`) tilsidesætter den for den kørsel.
- Ændring af indstillingen påvirker aldrig filer, du allerede har; slet dem med
  [`DELETE /requests/{id}/files`](#delete-files).
- Når API-saldoen ikke kan betale for lagring, opkræves der ikke noget, og du får en e-mail: fyld saldoen op inden for
  30 dage, ellers bliver de opbevarede filer midlertidige og slettes 7 dage senere.

I det sjældne tilfælde, hvor vores kopi mislykkes, har posten `"stored": false`, og URL'en er den oprindelige, kortvarige:
download den snart.
