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.
Events empfangen
Abschnitt betitelt „Events empfangen“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
datakönnen neue Felder bekommen.
Signaturen prüfen
Abschnitt betitelt „Signaturen prüfen“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 standardwebhooksfrom 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 standardwebhooksimport { 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-webhooksuse 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 nachwhsec_und vergleichen Sie das Ergebnis zeitkonstant (etwa mithmac.compare_digest). Lehnen Sie außerdem Zeitstempel ab, die mehr als ein paar Minuten von der aktuellen Zeit abweichen.
Event-Typen
Abschnitt betitelt „Event-Typen“| 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.
Aktuellen Stand nachladen
Abschnitt betitelt „Aktuellen Stand nachladen“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:
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.
Wiederholungen und automatische Deaktivierung
Abschnitt betitelt „Wiederholungen und automatische Deaktivierung“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.
Idempotenz und Reihenfolge
Abschnitt betitelt „Idempotenz und Reihenfolge“Zustellversuche können sich in seltenen Fällen überschneiden, und die Reihenfolge der Zustellungen ist nicht garantiert. Verarbeiten Sie Events deshalb idempotent:
id(gleichwebhook-id) ist bei jedem Zustellversuch desselben Events identisch. Haben Sie eine ID schon verarbeitet, bestätigen Sie mit 2xx und tun nichts weiter.occurred_atist UTC mit Mikrosekunden und lexikografisch sortierbar: Damit bringen Sie Events nachträglich in ihre tatsächliche Reihenfolge.
Testevents senden
Abschnitt betitelt „Testevents senden“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.
Signing Secret rotieren
Abschnitt betitelt „Signing Secret rotieren“Ü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.
Fehlersuche
Abschnitt betitelt „Fehlersuche“- 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.