Zum Hauptinhalt springen

Webhooks-Anfrage

Wenn ein Webhook-Ereignis ausgelöst wird, sendet Logto eine POST-Anfrage an jeden Endpunkt, der darauf abonniert ist. Den vollständigen Ereigniskatalog findest du unter Webhooks-Ereignisse; auf dieser Seite wird die Struktur der Anfrage dokumentiert, die Logto ausliefert.

Anfrage-Header

KeyAnpassbarHinweise
user-agentStandardmäßig Logto (https://logto.io/).
content-typeStandardmäßig application/json.
logto-signature-sha-256Signatur des Anfrage-Bodys. Siehe Webhooks absichern.

Anpassbare Header können über die Secure Webhook-Konfiguration überschrieben werden.

Überblick über den Anfrage-Body

Der Body ist ein JSON-Objekt. Seine genaue Struktur hängt davon ab, zu welcher Familie das Ereignis gehört:

FamilieEreignisseWann es ausgelöst wird
User flowPostRegister, PostSignIn, PostResetPasswordEin Benutzer schließt einen Anmelde-, Anmelde- oder Passwort-Reset-Flow ab, der von der Experience API verarbeitet wird.
Data mutationUser.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*Das zugrundeliegende Datenmodell wird durch einen Management API-Aufruf oder einen User Flow auf der Experience API verändert.
ExceptionIdentifier.Lockout, Message.RateLimited, Grant.LimitExceededEin Sicherheitsvorfall, z. B. ein Konto wird nach aufeinanderfolgenden fehlgeschlagenen Verifizierungsversuchen gesperrt.

Jede Familie teilt sich einen kleinen Satz von gemeinsamen Feldern. Jede Familie ergänzt dann eigene Kontextfelder sowie eine ereignisspezifische Nutzlast.

Gemeinsame Felder

In jeder Auslieferung unabhängig von der Familie enthalten:

FeldTypOptionalHinweise
hookIdstringDie Webhook-Konfigurations-ID in Logto.
eventstringDas Ereignis, das diese Auslieferung ausgelöst hat.
createdAtstringDie Erstellungszeit der Nutzlast im ISO 8601-Format.
userAgentstringDer User-Agent der auslösenden Anfrage.

Jede Familie enthält außerdem die IP-Adresse der auslösenden Anfrage, unter dem Feldnamen userIp für User-Flow-Ereignisse und ip für Data-Mutation- und Exception-Ereignisse. Die Semantik ist identisch; der historische Namensunterschied bleibt aus Gründen der Rückwärtskompatibilität erhalten.

User-Flow-Ereignis-Nutzlasten

Ereignisse: PostRegister, PostSignIn, PostResetPassword.

Wird ausgelöst, wenn ein Benutzer einen Anmelde-, Anmelde- oder Passwort-Reset-Flow abschließt, der von der Experience API verarbeitet wird. Zusätzlich zu den gemeinsamen Feldern enthält der Body:

FeldTypOptionalHinweise
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'Der User-Flow-Ereignistyp. Entspricht jeweils PostSignIn / PostRegister / PostResetPassword. Der Feldname behält die historische "interaction"-Benennung.
sessionIdstringDie Session-ID (nicht Interaction-ID) für dieses Ereignis, falls zutreffend.
userIpstringDie IP-Adresse der auslösenden Anfrage.
userIdstringDie Benutzer-ID, die mit diesem Ereignis verknüpft ist, falls zutreffend.
userUserEntityDie Benutzer-Entität, die mit diesem Ereignis verknüpft ist, falls zutreffend.
applicationIdstringDie Anwendungs-ID, die mit diesem Ereignis verknüpft ist, falls zutreffend.
applicationApplicationEntityDie Anwendungs-Entität, die mit diesem Ereignis verknüpft ist, falls zutreffend.

Entitätsstrukturen

type UserEntity = {
id: string;
username?: string;
primaryEmail?: string;
primaryPhone?: string;
name?: string;
avatar?: string;
customData?: object;
identities?: object;
lastSignInAt?: string;
createdAt?: string;
applicationId?: string;
isSuspended?: boolean;
};
enum ApplicationType {
Native = 'Native',
SPA = 'SPA',
Traditional = 'Traditional',
MachineToMachine = 'MachineToMachine',
Protected = 'Protected',
SAML = 'SAML',
}

type ApplicationEntity = {
id: string;
type: ApplicationType;
name: string;
description?: string;
};

Siehe Benutzer und Anwendungen für die vollständige Feldreferenz.

Data-Mutation-Ereignis-Nutzlasten

Ereignisse: alle Ereignisse unter User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*. Siehe Webhooks-Ereignisse → Data-Mutation-Webhook-Ereignisse für den vollständigen Katalog.

Der Body enthält immer:

  • Die gemeinsamen Felder.
  • Ein ip-Feld, die IP-Adresse der auslösenden Anfrage (optional, vorhanden wenn bekannt).
  • Einen API-Kontext, der beschreibt, wie die Änderung ausgelöst wurde. Der Kontext ist eine von zwei Varianten, abhängig von der Auslösequelle:
  • Eine ereignisspezifische Nutzlast: die betroffene Entität in data und (bei einigen Ereignissen) zusätzliche Top-Level-Felder. Siehe ereignisspezifische Daten-Nutzlasten.

Experience API-Kontextfelder

Vorhanden, wenn die Änderung durch einen benutzerorientierten Flow auf der Experience API ausgelöst wurde, z. B. User.Created während der Registrierung oder User.Data.Updated bei Profilaktualisierungen.

FeldTypOptionalHinweise
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'Der User-Flow-Ereignistyp, der die Änderung ausgelöst hat. Feldname behält die historische "interaction"-Benennung.
sessionIdstringDie Session-ID (nicht Interaction-ID) für dieses Ereignis, falls zutreffend.
applicationIdstringDie Anwendungs-ID, falls zutreffend.
applicationApplicationEntityDie Anwendungs-Entität, falls zutreffend.

Management API-Kontextfelder

Vorhanden, wenn die Änderung durch einen Management API-Aufruf ausgelöst wurde.

FeldTypOptionalHinweise
pathstringDer Pfad des API-Aufrufs, der diesen Webhook ausgelöst hat.
methodstringDie HTTP-Methode des API-Aufrufs.
statusnumberDer Antwort-Statuscode des API-Aufrufs.
paramsobjectDie koa path params des API-Aufrufs.
matchedRoutestringDie koa matched route. Logto verwendet dieses Feld, um aktivierte Webhook-Ereignisfilter zuzuordnen.

Ereignisspezifische Daten-Nutzlasten

Jedes Data-Mutation-Ereignis enthält ein Top-Level-data-Feld mit der betroffenen Entität oder null, wenn die Änderung nicht als einzelne Entität zusammengefasst werden kann (Lösch- und Mitgliedschaftsereignisse). Einige Ereignisse enthalten außerdem ereignisspezifische Top-Level-Felder außerhalb von data; Organization.Membership.Updated ist ein solcher Fall, unten dokumentiert.

Benutzerereignisse

EreignisFeldTypOptionalHinweise
User.CreateddataUserEntityDie erstellte Benutzer-Entität.
User.Data.UpdateddataUserEntityDie aktualisierte Benutzer-Entität.
User.Deleteddatanull/

Rollenereignisse

type Role = {
id: string;
name: string;
description: string;
type: 'User' | 'MachineToMachine';
isDefault: boolean;
};
type Scope = {
id: string;
name: string;
description: string;
resourceId: string;
createdAt: number;
};
EreignisFeldTypOptionalHinweise
Role.CreateddataRoleDie erstellte Rollen-Entität.
Role.Data.UpdateddataRoleDie aktualisierte Rollen-Entität.
Role.Deleteddatanull/
Role.Scopes.UpdateddataScope[]Die aktualisierten Berechtigungen, die der Rolle zugewiesen sind.
Role.Scopes.UpdatedroleIdstringDie Rollen-ID, der Berechtigungen zugewiesen werden. (Nur verfügbar, wenn das Ereignis durch das Erstellen einer Rolle mit zugewiesenen Berechtigungen ausgelöst wurde.)

Berechtigungs- (Scope-)Ereignisse

EreignisFeldTypOptionalHinweise
Scope.CreateddataScopeDie erstellte Berechtigungs-Entität.
Scope.Data.UpdateddataScopeDie aktualisierte Berechtigungs-Entität.
Scope.Deleteddatanull/

Organisationsereignisse

type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
EreignisFeldTypOptionalHinweise
Organization.CreateddataOrganizationDie erstellte Organisations-Entität.
Organization.Data.UpdateddataOrganizationDie aktualisierte Organisations-Entität.
Organization.Deleteddatanull/
Organization.Membership.Updateddatanull/Die Änderung wird durch optionale Top-Level-Delta-Arrays beschrieben. Siehe Organization.Membership.Updated-Nutzlast unten.
Organization.Membership.Updated-Nutzlast

Zusätzlich zu den gemeinsamen Feldern und den API-Kontextfeldern, die für die Auslösequelle gelten (Management API-Kontext für Management API-Routen, Experience API-Kontext für Just-in-Time-Bereitstellung), enthält das Ereignis Organization.Membership.Updated eine organizationId sowie optionale Delta-Arrays auf Top-Level-Ebene der Nutzlast (neben event, createdAt usw., nicht innerhalb von data, das für dieses Ereignis immer null ist).

FeldTypOptionalHinweise
organizationIdstringDie Organisation, deren Mitgliedschaft sich geändert hat.
addedUserIdsstring[]Benutzer-IDs, die durch diesen Auslöser neu hinzugefügt wurden. Fehlt, wenn keine Benutzer hinzugefügt wurden oder die Änderung keine Benutzer betrifft.
removedUserIdsstring[]Benutzer-IDs, die durch diesen Auslöser entfernt wurden. Fehlt, wenn keine Benutzer entfernt wurden.
addedApplicationIdsstring[]Anwendungs-IDs, die neu hinzugefügt wurden. Fehlt, wenn keine Anwendungen hinzugefügt wurden oder die Änderung keine Anwendungen betrifft.
removedApplicationIdsstring[]Anwendungs-IDs, die entfernt wurden. Fehlt, wenn keine Anwendungen entfernt wurden.

Die vier Delta-Arrays sind optional und additiv: Sie verändern nicht die bestehende Nutzlaststruktur für Konsumenten, die sie nicht erwarten, und das Legacy-Feld data: null wird weiterhin unverändert ausgegeben.

Auslöser und welche Delta-Felder sie ausgeben können
AuslöserMögliche Delta-Felder
POST /organizations/:id/usersaddedUserIds
PUT /organizations/:id/usersaddedUserIds, removedUserIds
DELETE /organizations/:id/users/:userIdremovedUserIds
POST /organizations/:id/applicationsaddedApplicationIds
PUT /organizations/:id/applicationsaddedApplicationIds, removedApplicationIds
DELETE /organizations/:id/applications/:applicationIdremovedApplicationIds
PUT /organization-invitations/:id/status (Accepted)addedUserIds
Just-in-Time-Bereitstellung beim Hinzufügen des Benutzers zu einer neuen OrganisationaddedUserIds
Leere Deltas werden ausgelassen (abwesend ≠ leere Änderung)

Leere Delta-Arrays werden vollständig aus der Nutzlast ausgelassen. Zum Beispiel erzeugt ein PUT /organizations/:id/users, das die Mitgliedschaftsmenge durch die bestehende Menge ersetzt, keine echte Änderung, und die Nutzlast reduziert sich auf nur { organizationId } mit allen vier Delta-Feldern abwesend. Gleiches gilt für das erneute Hinzufügen eines bestehenden Mitglieds und das erneute Akzeptieren einer Einladung durch einen Benutzer, der bereits Mitglied ist.

Konsumenten müssen ein fehlendes Feld als "keine Änderung auf dieser Seite" behandeln, nicht als "leere Änderung".

Pro-Array-Limit (stilles Abschneiden)

Jedes Delta-Array ist auf 5000 Einträge begrenzt. Wenn ein einzelner Management API-Aufruf mehr als 5000 Benutzer (oder Anwendungen) in einer Operation hinzufügt oder entfernt, wird das entsprechende Delta-Array stillschweigend auf die ersten 5000 Einträge gekürzt. Es gibt keinen Marker in der Nutzlast, der anzeigt, dass das Limit erreicht wurde.

Wenn deine Anwendung administrative Massenoperationen durchführt, die plausibel mehr als 5000 Mitglieder in einem Aufruf betreffen können, behandle ein Array mit genau 5000 Einträgen als Signal, die autoritative Mitgliedschaft über die Management API abzugleichen:

  • GET /organizations/:id/users: vollständige Benutzer-Mitgliedschaft.
  • GET /organizations/:id/applications: vollständige Anwendungs-Mitgliedschaft.

Dies folgt demselben Muster wie das push-Ereignis von GitHub, das commits auf 20 Einträge begrenzt und Konsumenten auf die Compare-API für die vollständige Liste verweist.

No-Op-Ereignisse überspringen

Um No-Op-Auslieferungen (Ereignisse ohne Delta-Felder) auf der Konsumentenseite zu überspringen, filtere nach Vorhandensein von Delta-Arrays:

if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// echte Mitgliedschaftsänderung, verarbeite sie
}

?.length ist sowohl für undefined als auch für [] falsch, sodass derselbe Ausdruck robust ist, egal ob das Feld fehlt oder (in einer hypothetischen Zukunft) als leeres Array ausgegeben wird.

Beispiel-Nutzlasten

Benutzer hinzufügen (POST /organizations/:id/users):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedUserIds": ["u_001"]
}

Benutzermitgliedschaft ersetzen (PUT /organizations/:id/users):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedUserIds": ["u_002"],
"removedUserIds": ["u_001"]
}

Benutzer entfernen (DELETE /organizations/:id/users/:userId):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_001"]
}

Anwendung hinzufügen (POST /organizations/:id/applications):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedApplicationIds": ["app_xyz"]
}

Bestehendes Mitglied erneut hinzufügen, No-Op-PUT oder erneutes Akzeptieren einer bereits bestehenden Einladung (keine echte Änderung):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc"
}

Massenoperation, die das 5000er-Limit erreicht (still gekürzt):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_0001", "u_0002", "/* … genau 5000 Einträge insgesamt */"]
}

Das Erkennen eines Arrays mit genau 5000 Einträgen sollte einen Abgleich per GET /organizations/:id/users (oder /applications) auslösen.

Organisationsrollen-Ereignisse

type OrganizationRole = {
id: string;
name: string;
description?: string;
};
type OrganizationScope = {
id: string;
name: string;
description?: string;
};
EreignisFeldTypOptionalHinweise
OrganizationRole.CreateddataOrganizationRoleDie erstellte Organisationsrollen-Entität.
OrganizationRole.Data.UpdateddataOrganizationRoleDie aktualisierte Organisationsrollen-Entität.
OrganizationRole.Deleteddatanull/
OrganizationRole.Scopes.Updateddatanull/
OrganizationRole.Scopes.UpdatedorganizationRoleIdstringDie Rollen-ID, der Berechtigungen zugewiesen werden. (Nur verfügbar, wenn das Ereignis durch das Erstellen einer Rolle mit zugewiesenen Berechtigungen ausgelöst wurde.)

Organisationsberechtigungs- (Scope-)Ereignisse

EreignisFeldTypOptionalHinweise
OrganizationScope.CreateddataOrganizationScopeDie erstellte Organisationsberechtigungs-Entität.
OrganizationScope.Data.UpdateddataOrganizationScopeDie aktualisierte Organisationsberechtigungs-Entität.
OrganizationScope.Deleteddatanull/

Ausnahme-Ereignis-Nutzlasten

Ereignisse: Identifier.Lockout, Message.RateLimited, Grant.LimitExceeded.

Wird bei Sicherheitsvorfällen ausgelöst, z. B. wenn ein Konto nach aufeinanderfolgenden fehlgeschlagenen Verifizierungsversuchen gesperrt wird oder Grants entfernt werden, weil das gleichzeitige Geräte-Limit einer App überschritten wurde.

Jedes Ausnahme-Ereignis enthält die gemeinsamen Felder und ein ip-Feld (gleiche Struktur wie bei Data-Mutation-Ereignissen). Die übrigen Felder hängen vom Ereignis ab.

Identifier.Lockout

Stammt aus einem benutzerorientierten Flow, daher enthält der Body auch die Experience API-Kontextfelder sowie:

enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
FeldTypOptionalHinweise
typeSignInIdentifierDer Identifikatortyp des Benutzers, z. B. E-Mail, Telefon oder Benutzername.
valuestringDer Identifikatorwert des Benutzers, der die Sperrung ausgelöst hat.

Message.RateLimited

Stammt aus einem benutzerorientierten Flow, daher enthält der Body auch die Experience API-Kontextfelder sowie:

FeldTypOptionalHinweise
actionstringDie rate-limitierte Aktion, z. B. VerificationCodeSend.
recipientstringDie E-Mail-Adresse oder Telefonnummer, die das Send-Rate-Limit erreicht hat.

Grant.LimitExceeded

Wird ausgelöst, wenn eine erfolgreiche Autorisierung einen Benutzer über das maximal zulässige gleichzeitige authentifizierte Geräte-Limit (maxAllowedGrants) einer App hinausbringt und Logto deren älteste Grants für diese App widerruft.

Dieses Ereignis wird vom OIDC-Autorisierungsendpunkt und nicht von der Experience API ausgelöst, daher enthält es nicht interactionEvent oder sessionId. Neben den gemeinsamen Feldern und ip enthält der Body:

FeldTypOptionalHinweise
userIdstringDer Benutzer, dessen Grants widerrufen wurden.
applicationIdstringDie Anwendung, deren maxAllowedGrants-Limit überschritten wurde.
applicationApplicationEntityDie Anwendungs-Entität. Fehlt, wenn die Anwendung zum Zeitpunkt der Auslieferung nicht aufgelöst werden kann.
maxAllowedGrantsnumberDas zum Zeitpunkt des Ereignisses konfigurierte Limit auf der Anwendung.
preRevocationActiveGrantCountnumberDie Anzahl aktiver Grants, die der Benutzer für diese Anwendung vor dem Widerruf hielt, einschließlich des gerade ausgestellten.
revokedGrantIdsstring[]Die IDs der tatsächlich widerrufenen Grants, älteste zuerst.

Beispiel-Nutzlast:

{
"hookId": "hook_abc",
"event": "Grant.LimitExceeded",
"createdAt": "2024-01-01T00:00:00.000Z",
"ip": "192.168.0.1",
"userAgent": "Mozilla/5.0",
"userId": "u_001",
"applicationId": "app_xyz",
"application": {
"id": "app_xyz",
"type": "SPA",
"name": "My app",
"description": "My app description"
},
"maxAllowedGrants": 2,
"preRevocationActiveGrantCount": 3,
"revokedGrantIds": ["grant_001"]
}

Hinweise zur Auslieferung:

  • Die widerrufenen Grant-Datensätze werden im Rahmen des Widerrufs zerstört, sodass die IDs in revokedGrantIds nicht mehr über die Grant-Listing-Endpunkte oder die Konsole aufgelöst werden können — diese geben nur aktive Grants zurück. Behandle die Nutzlast als Datensatz und speichere die IDs auf deiner Seite, wenn du sie später benötigst.
  • Das Ereignis wird nur ausgelöst, wenn mindestens ein Grant tatsächlich widerrufen wurde. Eine Autorisierung, die innerhalb des Limits bleibt, erzeugt kein Ereignis.
  • Es wird bei jeder Autorisierung ausgelöst, die das Limit überschreitet, sodass ein Benutzer, der sich wiederholt von mehr Geräten als erlaubt anmeldet, pro Entfernung ein Ereignis erhält.
  • Die Zustellung erfolgt nach dem Fire-and-Forget-Prinzip: Ein langsamer oder fehlerhafter Endpunkt blockiert oder verhindert niemals die Autorisierung des Benutzers. Fehlgeschlagene Auslieferungen werden wie jeder andere Webhook im Audit-Log aufgezeichnet.