---
title: Callbacks | RunAPI
description: Task-Callback-Lieferungen sicher empfangen und verifizieren.
url: https://runapi.ai/de/docs/guides/task-api/callbacks.md
canonical: https://runapi.ai/de/docs/guides/task-api/callbacks
locale: de
---

> HTML-Version: https://runapi.ai/de/docs/guides/task-api/callbacks
> Website-Index für KI-Agenten: https://runapi.ai/llms.txt

# Callbacks

Verwende Callbacks, um Task-Lifecycle-Ereignisse an einem öffentlichen HTTPS-
Endpunkt zu empfangen. Überprüfe jeden Callback, bevor du seinen JSON-Body verarbeitest, damit
nur für dein Konto signierte Zustellungen den Anwendungszustand ändern können.

## Eine Callback-URL konfigurieren

Fügen Sie beim Erstellen einer Task eine öffentliche HTTPS-`callback_url` hinzu. Der Ereignisinhalt und die Lebenszyklusstatus hängen vom Endpunkt ab; entnehmen Sie die Callback-Nutzlasten der API-Referenz des jeweiligen Endpunkts.

```json
{
  "model": "flux-2-pro-text-to-image",
  "prompt": "A product photograph on a clean studio background",
  "callback_url": "https://your-domain.com/webhooks/runapi"
}
```

## Ein Callback-Secret erstellen

Folgen Sie dem [Authentifizierungsleitfaden](https://runapi.ai/de/docs/guides/authentication.md), um sich anzumelden, öffnen Sie dann API-Schlüssel und erstellen Sie einen Callback-Secret für das Konto, das den Task erstellt. Speichern Sie den Wert in einem Secret-Manager und stellen Sie ihn nur Ihrem Callback-Empfänger zur Verfügung. Es handelt sich nicht um einen API-Schlüssel und darf niemals in einer Task-Anfrage gesendet werden.

Das Secret signiert Callbacks für dieses Konto. Durch Rotieren ändert sich die
Signatur für spätere Lieferungen. Aktualisieren Sie daher sofort alle Callback-Empfänger
und halten Sie beide Werte nur so lange verfügbar, wie es zur Verarbeitung
laufender Lieferungen nötig ist.

## Eine Callback-Signatur überprüfen

Jeder Callback ist ein HTTP-`POST` mit `Content-Type: application/json`. RunAPI fügt dieser Anfrage keinen `Authorization`-Header hinzu. Überprüfen Sie diese Header vor der Deserialisierung des Bodys:

| Header | Bedeutung |
|----------
| `X-Callback-Id` | Eindeutiger Bezeichner für diesen Zustellversuch. |
| `X-Callback-Timestamp` | Unix-Zeitstempel in Sekunden zum Zeitpunkt der Signierung der Zustellung. |
| `X-Callback-Signature` | Base64-kodierte HMAC-SHA-256-Signatur. |

Erstellen Sie den signierten Wert genau wie folgt, indem Sie die unveränderten Anfrage-Body-Bytes verwenden:

```text
X-Callback-Id + "." + X-Callback-Timestamp + "." + raw request body
```

Decodieren Sie das Callback-Secret mit Base64, berechnen Sie HMAC-SHA-256 über diesen Wert, codieren Sie das Ergebnis mit Base64 und vergleichen Sie es mit
`X-Callback-Signature` mittels eines zeitkonstanten Vergleichs. Parsen und re-serialisieren Sie das JSON nicht, bevor Sie es verifizieren.

### JavaScript

```javascript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCallback({headers, rawBody, callbackSecret}) {
  const callbackId = headers["x-callback-id"];
  const timestamp = Number(headers["x-callback-timestamp"]);
  const signature = headers["x-callback-signature"];

  if (!callbackId || !signature || !Number.isSafeInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const signedContent = `${callbackId}.${timestamp}.${rawBody}`;
  const expected = createHmac("sha256", Buffer.from(callbackSecret, "base64"))
    .update(signedContent, "utf8")
    .digest();
  const received = Buffer.from(signature, "base64");

  return expected.length === received.length && timingSafeEqual(expected, received);
}
```

Übergeben Sie den rohen Request-Body-String des Frameworks an `rawBody`; rufen Sie
`JSON.stringify` nicht auf geparsten Daten auf.

### Python

```python
import base64
import hashlib
import hmac
import time


def verify_callback(headers, raw_body, callback_secret):
    callback_id = headers.get("X-Callback-Id")
    timestamp = headers.get("X-Callback-Timestamp")
    signature = headers.get("X-Callback-Signature")

    if not callback_id or not timestamp or not signature:
        return False

    try:
        timestamp = int(timestamp)
        secret = base64.b64decode(callback_secret, validate=True)
        received = base64.b64decode(signature, validate=True)
    except (ValueError, TypeError):
        return False

    if abs(time.time() - timestamp) > 300:
        return False

    signed_content = f"{callback_id}.{timestamp}.{raw_body}".encode("utf-8")
    expected = hmac.new(secret, signed_content, hashlib.sha256).digest()
    return hmac.compare_digest(expected, received)
```

Übergeben Sie den exakten rohen Request-Body-String, den Ihr HTTP-Framework
empfangen hat, als `raw_body`.

## Lieferungen sicher verarbeiten

* Senden Sie eine `2xx`-Antwort nur dann, wenn der Callback zur Verarbeitung akzeptiert wurde.
  Nach einer Nicht-`2xx`-Antwort oder einem Transportfehler wird die Zustellung bis zu 10-mal
  erneut versucht.
* Antworten Sie innerhalb von 15 Sekunden. Stellen Sie langsamere Aufgaben nach der Überprüfung
  in eine Warteschlange, anstatt die HTTP-Antwort zu blockieren.
* Weisen Sie Callbacks mit fehlenden Signatur-Headern, einer ungültigen Signatur oder einem
  Zeitstempel außerhalb des von Ihrem Empfänger definierten Toleranzfensters ab.

## Überprüfung debuggen

* **Signaturkonflikt:** Vergewissern Sie sich, dass das Callback-Secret zum selben Konto wie die Aufgabe gehört, dekodieren Sie es mit Base64 und signieren Sie den ursprünglichen Body anstelle von geparsetem JSON.
* **Fehlende Signatur-Header:** Erstellen Sie ein Callback-Secret, bevor Sie Callbacks für Statusänderungen verwenden.
* **Zeitstempel abgelehnt:** Synchronisieren Sie die Uhr des Empfängers und verwenden Sie eine für Ihre Bereitstellung geeignete Toleranz.

Für Einrichtungs- und Lebenszyklushinweise kehren Sie zum [Task API
Quickstart](https://runapi.ai/de/docs/guides/task-api/quickstart.md) zurück.

---

## Mehr von RunAPI

- [Startseite](https://runapi.ai/de/.md)
- [Modellkatalog](https://runapi.ai/de/models.md)
- [Preise](https://runapi.ai/de/pricing.md)
- [Anbieter](https://runapi.ai/de/models)
- [Dokumentation](https://runapi.ai/de/docs/guides)
- [SDKs](https://runapi.ai/de/sdk.md)
- [CLI](https://runapi.ai/de/cli.md)
- [MCP Server](https://runapi.ai/de/mcp.md)
- [Claude Code vs Cursor](https://runapi.ai/de/claude-code-vs-cursor.md)
- [Cursor API-Einrichtung](https://runapi.ai/de/cursor-api-setup.md)
- [RunAPI vs OpenRouter](https://runapi.ai/de/openrouter-alternative.md)
- [Enterprise](https://runapi.ai/de/contact.md)
- [Kontakt](https://runapi.ai/de/contact.md)
- [Bedingungen](https://runapi.ai/de/terms.md)
- [Datenschutz](https://runapi.ai/de/privacy.md)
- [Website-Index für KI-Agenten](https://runapi.ai/llms.txt)

Kontakt: contact@runapi.ai

## Strukturierte Daten

```json
[
  {
    "@context": "https://schema.org",
    "inLanguage": "de",
    "@type": "WebSite",
    "name": "RunAPI",
    "url": "https://runapi.ai/de",
    "potentialAction": {
      "@type": "SearchAction",
      "target": {
        "@type": "EntryPoint",
        "urlTemplate": "https://runapi.ai/de/models?q={search_term_string}"
      },
      "query-input": "required name=search_term_string"
    }
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "de",
    "@type": "Organization",
    "name": "RunAPI",
    "url": "https://runapi.ai/de",
    "logo": {
      "@type": "ImageObject",
      "url": "https://runapi.ai/deicon.svg"
    },
    "sameAs": [
      "https://github.com/runapi-ai"
    ]
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "de",
    "@type": "TechArticle",
    "headline": "Callbacks",
    "description": "Task-Callback-Lieferungen sicher empfangen und verifizieren.",
    "url": "https://runapi.ai/de/docs/guides/task-api/callbacks",
    "mainEntityOfPage": "https://runapi.ai/de/docs/guides/task-api/callbacks"
  }
]
```
