Webhooks
Usher kann deinen Servern mitteilen, was in einer Umgebung passiert, sobald es passiert: Jemand kommt dazu, meldet sich an, ändert seine Angaben oder geht. Jedes Ereignis ist ein signierter POST an eine Adresse deiner Wahl, der wiederholt wird, wenn dein Server nicht antwortet.
Füge einen Endpunkt hinzu
Öffne in Usher Console die Umgebung und dann Webhooks → Neuer Endpunkt. Gib eine https://-Adresse von dir an und wähle die Ereignisse, über die sie informiert werden soll. Console zeigt das Signatur-Secret des Endpunkts (whsec_…) nur einmal an: Bewahre es bei den anderen Secrets deines Servers auf.
| Ereignis | Wann |
|---|---|
user.created | Jemand hat ein Konto erstellt |
user.updated | Ein Konto hat sich geändert: E-Mail-Adresse, Name, Bestätigung oder Sperre (auch eine Sperre durch das Verzeichnis deiner Organisation, über SCIM) |
user.deleted | Ein Konto wurde gelöscht |
session.created | Jemand hat sich angemeldet |
session.ended | Eine Sitzung ist zu Ende: abgemeldet, widerrufen oder vorzeitig abgelaufen |
Test senden schickt ein webhook.test-Ereignis an genau diesen Endpunkt, ganz gleich, welche Ereignisse er abonniert hat.
Was ankommt
POST /usher/webhooks HTTP/1.1
content-type: application/json
user-agent: Usher-Webhooks/1
webhook-id: msg_2fk7x…
webhook-timestamp: 1793643116
webhook-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE={
"type": "user.created",
"timestamp": "2026-11-02T18:11:56.412Z",
"data": {
"user": {
"id": "Qz3m…",
"email": "lena@acme.com",
"emailVerified": false,
"name": "Lena Live",
"image": null,
"banned": false,
"createdAt": "2026-11-02T18:11:56.380Z",
"updatedAt": "2026-11-02T18:11:56.380Z"
}
}
}Ereignisse beschreiben Menschen und Sitzungen, nie Secrets: keine Tokens, keine Passwort-Hashes, keine Rollen und auch nicht, warum jemand gesperrt wurde.
Prüfe jede Zustellung
Usher signiert nach der Spezifikation Standard Webhooks, deshalb prüft jede ihrer Bibliotheken die Signatur für dich. Lehne alles ab, was sich nicht verifizieren lässt. Die Bibliotheken lehnen außerdem Zeitstempel ab, die mehr als fünf Minuten abweichen, sodass sich eine alte Zustellung nicht erneut abspielen lässt.
// a route that receives Usher's webhooks (Node, the standardwebhooks package)
import { Webhook } from 'standardwebhooks'
const wh = new Webhook(process.env.USHER_WEBHOOK_SECRET!) // whsec_…, from Usher Console
export async function POST(request: Request) {
const body = await request.text() // the raw body: the signature covers it byte for byte
let event: { type: string; timestamp: string; data: Record<string, unknown> }
try {
event = wh.verify(body, Object.fromEntries(request.headers)) as typeof event
} catch {
return new Response('bad signature', { status: 400 })
}
// webhook-id is the same on every attempt: skip one you have handled already
if (await alreadyHandled(request.headers.get('webhook-id')!)) return new Response(null, { status: 204 })
await queue(event) // answer quickly; do the work afterwards
return new Response(null, { status: 204 })
}- Antworte innerhalb von 15 Sekunden mit einem beliebigen
2xx. Alles andere, auch eine Weiterleitung, zählt als Fehlschlag. - Rechne mit Wiederholungen. Eine Nachricht behält bei jedem Versuch ihre
webhook-id; überspringe eine, die du schon verarbeitet hast. - Verlass dich nicht auf die Reihenfolge. Wiederholungen können nach neueren Ereignissen eintreffen.
Wenn dein Server nicht antwortet
Usher versucht es nach 5 Sekunden, 5 Minuten, 30 Minuten, 2 Stunden, 5 Stunden, 10 Stunden und noch einmal 10 Stunden erneut (insgesamt etwa anderthalb Tage) und gibt dann auf. Das Zustellprotokoll in Console zeigt für jede Nachricht alle Versuche und die letzte Antwort deines Servers, und Erneut senden startet eine Zustellung von vorn.
Ein neues Secret
Neues Secret in Console erzeugt ein weiteres und zeigt es einmal an. In den nächsten 24 Stunden trägt jede Zustellung zwei Signaturen, eine pro Secret, sodass du das neue Secret ausrollen kannst, ohne etwas zu verpassen. Danach signiert nur noch das neue.
Woher Zustellungen kommen
Webhooks verlassen Usher über seinen Egress-Proxy, der sich nicht mit privaten oder internen Adressen verbindet. Ein Endpunkt muss eine https://-Adresse mit einem öffentlichen Namen sein: keine IP-Adresse, kein privater Name und keine Adresse auf Ushers eigenen Domains.