> ## Documentation Index
> Fetch the complete documentation index at: https://docs.querying.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Zustell-Payload, Wiederholungsplan und wie Sie einen sicheren Handler schreiben.

Geben Sie bei einem Task `webhook.url` an, und wir senden das Ergebnis per POST dorthin, sobald der Task einen
Endzustand erreicht: `COMPLETED` oder `FAILED`.

```json theme={null}
{
  "taskType": "PERPLEXITY",
  "payload": { "prompt": "best wireless earbuds 2026" },
  "webhook": { "url": "https://your-server.example.com/hook" }
}
```

## Zustell-Payload

Wir senden per POST mit `Content-Type: application/json` diesen Body:

<ResponseField name="task" type="object" required>
  Dieselben Task-Metadaten wie beim Einreichen, jetzt mit einem End-`status` und Ihrem zurückgegebenen `idempotencyKey`.
</ResponseField>

<ResponseField name="credits" type="object" required>
  Die Credit-Kosten der Engine: `2` für Perplexity, `3` für ChatGPT usw. (1–3 Credits je nach Engine).
  `creditsCharged` ist `0`, wenn der Task fehlgeschlagen ist. Anders als die Antworten beim Einreichen und Pollen, die
  immer null melden, ist dieses Feld nicht null.
</ResponseField>

<ResponseField name="response" type="object" required>
  Das engine-spezifische Ergebnis. Siehe [Engines](/de/engines/overview). Bei einem fehlgeschlagenen Task steht hier
  `{ "error": "<reason>" }` statt des Engine-Ergebnisses.
</ResponseField>

```json theme={null}
{
  "task": {
    "id": "8f2c1e40-...",
    "taskType": "PERPLEXITY",
    "status": "COMPLETED",
    "priority": 1,
    "createdAt": "2026-07-09T04:12:00.000Z",
    "idempotencyKey": "job-42:prompt-7:perplexity"
  },
  "credits": { "creditsToCharge": 3, "creditsCharged": 3 },
  "response": {
    "text": "The best wireless earbuds in 2026 are ...",
    "sources": [{ "position": 1, "url": "https://...", "label": "..." }]
  }
}
```

Auch ein fehlgeschlagener Task wird zugestellt:

```json theme={null}
{
  "task": { "id": "8f2c1e40-...", "status": "FAILED", "...": "..." },
  "credits": { "creditsToCharge": 3, "creditsCharged": 0 },
  "response": { "error": "ENGINE_TIMEOUT: The engine did not answer before the task deadline." }
}
```

## Wiederholungen

Geben Sie zur Bestätigung einen beliebigen `2xx` zurück. Alles andere, auch ein Timeout, gilt als Fehlschlag, und wir
wiederholen mit exponentiellem Backoff:

| Versuch | Auslösung |
| - | - |
| 1 | sofort |
| 2 | nach 2 Minuten |
| 3 | nach 4 Minuten |
| 4 | nach 8 Minuten |
| 5 | nach 16 Minuten |

Nach dem fünften Versuch wird die Zustellung aufgegeben und protokolliert. Es gibt keine Dead-Letter-Queue, die Sie
lesen können. Um das Ergebnis über die Task-API abzurufen, pollen Sie innerhalb des 24-stündigen öffentlichen
Polling-Fensters. Dieses Fenster bestimmt nicht die Speicherdauer des zugrunde liegenden Datensatzes.

Jeder Versuch hat ein Anfrage-Timeout von **30 Sekunden**.

<Warning>
  Aus Sicht des Tasks ist die Zustellung „Fire and Forget“. Ein abgeschlossener Task, dessen Webhook nie zugestellt
  werden kann, zeigt in `GET /v1/async/task/{id}` weiterhin `COMPLETED`: Der Status beschreibt die Arbeit der Engine,
  nicht die Benachrichtigung.
</Warning>

## Einen sicheren Handler schreiben

<Steps>
  <Step title="Schnell bestätigen">
    Geben Sie `200` zurück, sobald Sie den Payload dauerhaft in eine Warteschlange gelegt haben. Parsen und
    Datenbankschreibvorgänge erledigen Sie danach. Ein langsamer Handler verbraucht das 30-Sekunden-Timeout und löst
    eine unerwünschte Wiederholung aus.
  </Step>

  <Step title="Über idempotencyKey deduplizieren">
    Wegen der Wiederholungen kann Ihr Handler denselben Task legitim mehrfach erhalten. Gleichen Sie über
    `task.idempotencyKey` (oder `task.id`) ab und machen Sie den Schreibvorgang idempotent.
  </Step>

  <Step title="Nach Status verzweigen, nicht nach Vorhandensein">
    Prüfen Sie `task.status === "FAILED"` explizit. Auch ein fehlgeschlagener Task stellt einen Webhook zu.
  </Step>
</Steps>

<Note>
  Webhook-Anfragen tragen weder Signatur noch gemeinsames Geheimnis. Ist Ihr Endpunkt öffentlich, behandeln Sie den
  Payload als nicht vertrauenswürdig und verwenden Sie einen nicht erratbaren URL-Pfad, oder stellen Sie den Handler
  hinter netzwerkseitige Beschränkungen.
</Note>
