# Anfragen

Eine Anfrage ist ein Aufruf eines Modells. Sie hat eine ID (`req_…`), einen Status, ihre Kosten in API-Credits (1000 = 1 USD) und, wenn sie erfolgreich ist, die generierten Dateien. Dasselbe Objekt wird von jedem unten aufgeführten Endpunkt für jedes Modell zurückgegeben.

## Ein Modell finden

Der Katalog aller 1.496 Modelle ist öffentliches JSON (kein Schlüssel erforderlich):

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

| Abfrageparameter | Bedeutung |
|---|---|
| `q` | Wörter, die alle im Namen, in der ID, beim Hersteller oder in den Tags vorkommen müssen, zum Beispiel `kling 3`. |
| `category` | Eine Kategorie-ID, zum Beispiel `text-to-video` oder `upscale`. `GET https://api.doitong.com/v1/categories` listet alle 21 mit ihren Anzahlen auf. |
| `lab` | Der Hersteller des Modells, nach Name oder ID, zum Beispiel `Google` oder `black-forest-labs`. |
| `family` | Eine `family`-ID aus einer vorherigen Zeile: jede Variante eines Modells (pro, fast, image-to-video, …). |
| `mode` | `exact` (der Preis wird aus der Eingabe berechnet), `measured` (aus der Länge oder Größe der Eingabedateien), `estimate` (ein geschätzter Preis pro Ausführung) oder `metered` (Abrechnung nach tatsächlicher Nutzung bis zu einem Maximum pro Ausführung). |
| `limit`, `cursor` | Seitengröße (1 bis 100, Standard 50) und der `next_cursor` der vorherigen Seite. |

Jede Zeile enthält `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` und ein `price`-Objekt (siehe [Preise](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` ergänzt das vollständige JSON-Schema für Eingabe und Ausgabe, die kanonischen Parameternamen, die Preisregeln und eine Beispiel-Eingabe. Eine unbekannte `category` oder ein unbekannter `mode` führt zu `400 invalid_parameter`.

## Ein Modell ausführen

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

- Der Body ist die Modell-Eingabe, ein JSON-Objekt. Jede Modellseite listet ihre Parameter auf.
- Die Eingabe wird geprüft, bevor etwas berechnet wird. Eine ungültige Eingabe gibt `422 invalid_input` mit dem fehlerhaften `param` zurück und kostet nichts.
- `?wait=N` (bis zu 20 Sekunden) hält den Aufruf an, bis die Anfrage abgeschlossen ist oder die Zeit abläuft.
- `?webhook_url=https://…` sendet einen signierten POST an diese URL, wenn die Anfrage abgeschlossen ist (siehe [Webhooks](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` oder `?storage=temporary` legt fest, wie lange die Ausgabe-Dateien aufbewahrt werden; ohne diesen Parameter gilt die Kontoeinstellung (siehe [Wo Dateien gespeichert werden](#storage)).

Die Antwort lautet `202 Accepted`, solange die Anfrage noch verarbeitet wird, und `200 OK`, wenn sie innerhalb der Wartezeit abgeschlossen wurde (oder wenn es sich um eine Wiederholung handelt, siehe unten). Die Header `Location` und `Doitong-Request-Id` enthalten die URL und ID der Anfrage.

## Status

Lebenszyklus einer Anfrage: `queued` → `running` → `succeeded` | `failed` | `canceled` (final; fehlgeschlagene und abgebrochene Anfragen werden erstattet).

| Status | Bedeutung | Final |
|---|---|---|
| `queued` | Akzeptiert und berechnet, wartet auf einen Worker. `queue_position` kann die Position in der Warteschlange anzeigen. | nein |
| `running` | Das Modell generiert. | nein |
| `succeeded` | Abgeschlossen. `outputs` listet die Dateien auf. | ja |
| `failed` | Das Modell oder ein Limit hat die Verarbeitung beendet. `error` erklärt den Grund. Die API-Credits werden erstattet. | ja |
| `canceled` | Abgebrochen, bevor ein Ergebnis erzeugt wurde. Die API-Credits werden erstattet. | ja |

Eine Anfrage, die länger als ihr Zeitlimit läuft, schlägt mit dem Code `timeout` fehl und wird erstattet: 15 Minuten für Bilder, 60 Minuten für Video, 30 Minuten für Audio und alles andere.

## Das Anfrageobjekt

```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` ist bei jedem Modell gleich: ein Eintrag pro generierter Datei mit `type` (`image`, `video`, `audio`, `file` oder `3d`), einer `url` in unserem Speicher, `content_type` und, falls bekannt, `file_size`, `width`, `height`, `duration`.
- `output` ist das eigene Ergebnisobjekt des Modells (seine Felder sind auf jeder Modellseite aufgeführt) mit denselben URLs.
- `storage` gibt an, wie die Dateien aufbewahrt werden: `mode` (`permanent` oder `temporary`), gespeicherte `bytes`, `expires_at` (Zeitpunkt, an dem eine temporäre Datei gelöscht wird, bei permanenten Dateien `null`) und `expired` (`true`, sobald die Dateien gelöscht sind).
- `cost.credits` ist der Preis des Aufrufs in API-Credits (`cost.usd` in USD), `cost.charged` der vom API-Guthaben abgebuchte Betrag und `cost.refunded` der erstattete Betrag. Bei einem `metered`-Modell ist `cost.credits` der zu Beginn reservierte Höchstbetrag (`cost.held`); sobald die Ausführung erfolgreich ist, gibt `cost.settled` die tatsächlichen Nutzungskosten an, und der Rest des Höchstbetrags steht in `cost.refunded` (siehe [Abrechnung nach Nutzung](https://doitong.com/developers/docs/pricing#metered)).
- Wenn eine Anfrage fehlschlägt, lautet `error` `{ "type": …, "code": …, "message": … }`.

## Eine Anfrage lesen

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

- `?wait=N` (bis zu 20 Sekunden) führt einen Long-Polling-Aufruf aus: Der Aufruf gibt die Antwort zurück, sobald die Anfrage abgeschlossen ist, oder nach N Sekunden mit dem aktuellen Status. Eine Schleife darüber ist die einfachste Möglichkeit, auf ein Ergebnis zu warten.
- `?logs=1` fügt die Fortschrittsprotokollzeilen des Modells (die letzten 50) als `logs` hinzu.

Du kannst nur Anfragen deines eigenen Kontos lesen; jede andere ID führt zu `404 request_not_found`.

## Anfragen auflisten

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

Neueste zuerst. `limit` reicht von 1 bis 100 (Standard 20), `status` und `model` filtern. Die Antwort lautet `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; übergib `cursor=<next_cursor>` für die nächste Seite.

## Eine Anfrage abbrechen

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

- Vor dem Start des Modells abgebrochen: Der Status wird zu `canceled`, die API-Credits werden erstattet und du erhältst `200`.
- Bereits in der Generierung: Der Abbruch wird weitergeleitet und du erhältst `202` mit `"cancel_requested": true`. Wenn das Modell trotzdem fertig wird, ist die Anfrage normal erfolgreich und wird berechnet.
- Bereits abgeschlossen: `409 not_cancelable` (eine bereits `canceled`-Anfrage gibt einfach `200` zurück).

## Die Dateien einer Anfrage löschen

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

Löscht jetzt unsere Kopien der Ausgabe-Dateien der Anfrage und beendet deren Speichergebühren. Die Antwort ist das Anfrageobjekt mit `storage.expired: true`; die URLs bleiben im Datensatz erhalten, funktionieren aber nicht mehr. Ein erneuter Aufruf ändert nichts (`200`). Eine Anfrage mit Status `queued` oder `running` hat noch keine Dateien: `409 request_active`. Erfordert `models:run`.

## Idempotenz: ohne doppelte Kosten wiederholen

Netzwerke fallen aus. Um eine Ausführung sicher zu wiederholen, sende einen `Idempotency-Key`-Header (beliebiger String mit bis zu 255 Zeichen, pro Vorgang eindeutig, zum Beispiel deine eigene Bestell-ID):

- Derselbe Schlüssel und derselbe Body wie zuvor: Du erhältst die ursprüngliche Anfrage zurück (`200`, Header `Idempotent-Replayed: true`), und es wird nichts erneut berechnet.
- Derselbe Schlüssel und Body nach einer Ausführung, die fehlschlug, bevor sie das Modell erreichte (`upstream_error` oder `internal_error` beim Absenden, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): Es wurde nichts erzeugt und nichts berechnet, daher führt die Wiederholung das Modell erneut aus, statt den Fehler zurückzugeben.
- Derselbe Schlüssel mit einem anderen Body: `409 idempotency_mismatch`.
- Schlüssel gelten für den API-Schlüssel, der sie gesendet hat.

## Wo Dateien gespeichert werden

Jede Ausgabe-Datei wird in unseren Speicher kopiert, bevor die Anfrage als `succeeded` markiert wird. Daher befinden sich die URLs in `outputs` auf unseren eigenen Hosts und du kannst direkt auf sie verlinken. Wie lange sie aufbewahrt werden, entscheidest du:

| Modus | Dauer | Preis |
|---|---|---|
| `permanent` (Standard) | Bis du sie löschst | 100 API-Credits (0,10 $) pro GB und Monat, täglich vom API-Guthaben abgebucht |
| `temporary` | 7 Tage, danach gelöscht | Kostenlos |

- Die Kontoeinstellung gilt für neue Anfragen: der Schalter „Dateien dauerhaft speichern“ im [Dashboard](https://doitong.com/developers/dashboard#balance) oder `PATCH https://api.doitong.com/v1/account/storage` mit `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` zeigt die Einstellung unter `storage` zusammen mit den von dir gespeicherten Bytes und der monatlichen Schätzung an.
- `?storage=permanent|temporary` bei einer Ausführung (das `storage`-Argument des MCP-Tools `run`) überschreibt sie für diese Ausführung.
- Das Ändern der Einstellung betrifft vorhandene Dateien nicht; lösche sie mit [`DELETE /requests/{id}/files`](#delete-files).
- Wenn das API-Guthaben die Speicherkosten nicht decken kann, wird nichts berechnet und du erhältst eine E-Mail: Lade innerhalb von 30 Tagen auf, andernfalls werden die aufbewahrten Dateien temporär und 7 Tage später gelöscht.

Falls unsere Kopie ausnahmsweise fehlschlägt, enthält der Eintrag `"stored": false` und die URL ist die ursprüngliche, kurzlebige URL: Lade die Datei bald herunter.
