Zum Inhalt springen

Webhooks

Webhooks informieren Ihre Systeme über Events zu Benutzern und Kursteilnahmen, ohne dass Sie die API regelmäßig abfragen müssen: Schulungstool sendet signierte POST-Requests an eine HTTPS-URL, die Sie festlegen. Webhook-Endpunkte richten Sie in Ihrem Administrationsbereich unter Einstellungen > Erweiterte Einstellungen > Entwickleroptionen > Webhooks ein.

Alle Event-Typen mit Payload-Schema und Beispielen finden Sie in der API-Referenz im Abschnitt Webhooks.

Jedes Event kommt als POST-Request mit JSON-Body an Ihren Endpunkt. Der Umschlag ist bei allen Event-Typen gleich:

{
"id": "msg_7f3c0a1b2c3d4e5f60718293a4b5c6d7",
"type": "participation.completed",
"occurred_at": "2026-10-17T14:00:00.123456Z",
"api_version": "v1",
"tenant": "ihre-firma.schulungstool.de",
"tenant_id": "018c2a4e-9a6c-4e0d-8b8f-4c5d6e7f8a9b",
"data": {
"id": "018c2a4e-6d3f-4b7a-9e5c-1f2a3b4c5d6e",
"user": "018c2a4e-7e4a-4c8b-8f6d-2a3b4c5d6e7f",
"course": "018c2a4e-8f5b-4d9c-9a7e-3b4c5d6e7f8a"
}
}

Antworten Sie mit einem beliebigen 2xx-Status, sobald Sie das Event gespeichert haben. Jede andere Antwort, auch ein Redirect, zählt als Fehlschlag und wird wiederholt; einzige Ausnahme ist HTTP 410, das den Endpunkt sofort deaktiviert. Antworten Sie schnell: Nach 10 Sekunden gilt die Zustellung als fehlgeschlagen. Verarbeiten Sie aufwendige Logik deshalb asynchron, zum Beispiel über eine Queue, und bestätigen Sie den Empfang sofort.

Zwei Regeln machen Ihre Integration zukunftssicher:

  • Unbekannte Event-Typen ignorieren (mit 2xx bestätigen): Es können neue Typen hinzukommen.
  • Unbekannte Felder ignorieren: Umschlag und data können neue Felder bekommen.

Jede Zustellung ist nach der Standard-Webhooks-Spezifikation signiert und trägt drei Header:

Header Bedeutung
webhook-id Event-ID, bei allen Zustellversuchen identisch.
webhook-timestamp Unix-Zeitstempel (Sekunden) dieses Zustellversuchs.
webhook-signature Signatur(en), z. B. v1,K5oZfzN9.... Während einer Secret-Rotation stehen mehrere Signaturen durch Leerzeichen getrennt im Header; eine gültige genügt.

Das Signing Secret (whsec_...) sehen Sie beim Anlegen des Endpunkts und jederzeit über Signing Secret anzeigen. Prüfen Sie die Signatur mit einer der Standard-Webhooks-Bibliotheken, bevor Sie ein Event verarbeiten:

# pip install standardwebhooks
from standardwebhooks.webhooks import Webhook, WebhookVerificationError
webhook = Webhook("whsec_...")
def handle(raw_body: bytes, headers: dict):
try:
payload = webhook.verify(raw_body, headers)
except WebhookVerificationError:
return 400
# payload ist das verifizierte Event
return 204
// npm install standardwebhooks
import { Webhook } from "standardwebhooks";
const webhook = new Webhook("whsec_...");
function handle(rawBody: string, headers: Record<string, string>): number {
try {
webhook.verify(rawBody, headers);
} catch {
return 400;
}
return 204;
}
// composer require standard-webhooks/standard-webhooks
use StandardWebhooks\Webhook;
$webhook = new Webhook("whsec_...");
try {
$webhook->verify($rawBody, $headers);
} catch (\Exception $e) {
http_response_code(400);
exit;
}

Zwei Stolperfallen:

  • Signiert wird der rohe Request-Body. Verifizieren Sie die exakten Bytes, die ankommen, nicht neu serialisiertes JSON: Schon ein umsortiertes Feld oder ein zusätzliches Leerzeichen macht die Signatur ungültig.
  • Eigenbau braucht konstante Vergleichszeit. Wenn Sie ohne Bibliothek verifizieren, berechnen Sie HMAC-SHA256 über {webhook-id}.{webhook-timestamp}.{body} mit dem Base64-dekodierten Teil des Secrets nach whsec_ und vergleichen Sie das Ergebnis zeitkonstant (etwa mit hmac.compare_digest). Lehnen Sie außerdem Zeitstempel ab, die mehr als ein paar Minuten von der aktuellen Zeit abweichen.
Event-Typ Wird gesendet, wenn …
participation.created eine Kursteilnahme angelegt wird (auch durch wiederkehrende Schulungen, Import oder API).
participation.completed eine Kursteilnahme erledigt wird, egal durch wen.
participation.certificate_created das Zertifikat zu einer erledigten Kursteilnahme erzeugt wurde.
participation.deleted eine Kursteilnahme in den Papierkorb verschoben wird.
user.created ein Benutzerkonto angelegt wird (auch per Import, Selbstregistrierung oder API).
user.activated ein deaktiviertes Benutzerkonto wieder aktiviert wird.
user.deactivated ein Benutzerkonto deaktiviert wird.
user.deleted ein Benutzerkonto in den Papierkorb verschoben wird.
ping Sie in den Entwickleroptionen ein Testevent senden. data ist immer leer.

Jeder Endpunkt erhält nur die Event-Typen, die er abonniert hat. Schemas und Beispiele: API-Referenz, Abschnitt Webhooks.

Events transportieren bewusst nur IDs (und bei *.deleted den Löschzeitpunkt), keine vollständigen Objekte. So enthält Ihr Empfangs-Log keine personenbezogenen Snapshots, und Sie verarbeiten nie veraltete Zwischenstände. Den aktuellen Zustand laden Sie bei Bedarf mit Ihrem API-Schlüssel nach:

Terminal-Fenster
curl "https://api.schulungstool.de/v1/participations/018c2a4e-6d3f-4b7a-9e5c-1f2a3b4c5d6e/" \
-H "Authorization: Token <API-Schlüssel>"

Rechnen Sie dabei mit 404: Zwischen Event und Abruf kann das Objekt gelöscht worden sein. In dem Fall folgt ein eigenes *.deleted-Event.

Schlägt eine Zustellung fehl, wiederholt Schulungstool sie mit wachsenden Abständen: nach etwa 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 5 Stunden und dann alle 10 Stunden, insgesamt bis zu 8 Versuche. Antwortet Ihr Endpunkt mit 429 und einem Retry-After-Header (Sekunden), wird der nächste Versuch entsprechend verschoben.

Zwei Sonderfälle deaktivieren den Endpunkt automatisch:

  • Dauerhafte Fehlschläge: Scheitern alle Zustellungen an einen Endpunkt fünf Tage in Folge, wird er deaktiviert und die Administratoren werden per E-Mail informiert.
  • HTTP 410 (Gone): Antwortet der Endpunkt mit 410, wird er sofort und ohne E-Mail deaktiviert. Nutzen Sie das, um einen abgeschalteten Empfänger sauber abzumelden.

Events, die während einer Deaktivierung auftreten, werden nicht nachgeliefert. Nach dem Reaktivieren können Sie einzelne Zustellungen aus dem Zustellprotokoll erneut anstoßen.

Zustellversuche können sich in seltenen Fällen überschneiden, und die Reihenfolge der Zustellungen ist nicht garantiert. Verarbeiten Sie Events deshalb idempotent:

  • id (gleich webhook-id) ist bei jedem Zustellversuch desselben Events identisch. Haben Sie eine ID schon verarbeitet, bestätigen Sie mit 2xx und tun nichts weiter.
  • occurred_at ist UTC mit Mikrosekunden und lexikografisch sortierbar: Damit bringen Sie Events nachträglich in ihre tatsächliche Reihenfolge.

Unter Entwickleroptionen > Webhooks senden Sie über das Dreipunktmenü eines Endpunkts ein Testevent, wahlweise als ping oder als beliebiger Event-Typ mit festen Beispieldaten. Das Ergebnis samt Antwort Ihres Endpunkts erscheint sofort, und die Zustellung steht wie jede andere im Zustellprotokoll.

Über Signing Secret rotieren erzeugen Sie ein neues Secret. Das bisherige bleibt 24 Stunden gültig; in dieser Zeit trägt der webhook-signature-Header beide Signaturen, sodass Sie Ihren Empfänger ohne verpasste Events umstellen können. Die Standard-Webhooks-Bibliotheken akzeptieren automatisch jede gültige Signatur im Header.

  • Zustellprotokoll: Jeder Endpunkt hat ein Protokoll der letzten 30 Tage mit Request sowie Antwort, Statuscode und Fehlertext des jeweils letzten Zustellversuchs, filterbar nach Status, Event-Typ und Zeitraum. Einzelne Zustellungen können Sie dort erneut anstoßen.
  • Signatur ungültig: Prüfen Sie, ob Sie den rohen Body verifizieren (siehe oben) und ob nach einer Rotation noch das alte Secret hinterlegt ist.
  • Keine Events? Prüfen Sie, ob der Endpunkt aktiv ist und den Event-Typ abonniert hat, und senden Sie ein Testevent. Ihr Endpunkt muss eine öffentliche HTTPS-URL auf Port 443 sein.
  • Die Fehlerstruktur der API beim Nachladen beschreibt Fehler und Durchsatzbegrenzung.