Saltar al contenido principal

Solicitud de Webhooks

Cuando se activa un evento de webhook, Logto envía una solicitud POST a cada endpoint suscrito a él. El catálogo completo de eventos se encuentra en Eventos de Webhooks; esta página documenta la forma de la solicitud que Logto entrega.

Encabezados de la solicitud

KeyPersonalizableNotas
user-agentLogto (https://logto.io/) por defecto.
content-typeapplication/json por defecto.
logto-signature-sha-256Firma del cuerpo de la solicitud. Ver asegurando tus webhooks.

Los encabezados personalizables se pueden sobrescribir a través de la configuración de webhook seguro.

Resumen del cuerpo de la solicitud

El cuerpo es un objeto JSON. Su forma exacta depende de a qué familia pertenece el evento:

FamiliaEventosCuándo se activa
Flujo de usuarioPostRegister, PostSignIn, PostResetPasswordUn usuario completa un flujo de registro, inicio de sesión o restablecimiento de contraseña manejado por la Experience API.
Mutación de datosUser.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*El modelo de datos subyacente se muta, ya sea por una llamada a la Management API o por un flujo de usuario en la Experience API.
ExcepciónIdentifier.LockoutUn incidente de seguridad, por ejemplo, una cuenta bloqueada después de intentos fallidos consecutivos de verificación.

Cada familia comparte un pequeño conjunto de campos comunes. Luego, cada familia agrega sus propios campos de contexto de solicitud más una carga útil específica del evento.

Campos comunes

Presentes en cada entrega independientemente de la familia:

CampoTipoOpcionalNotas
hookIdstringEl identificador de configuración del webhook en Logto.
eventstringEl evento que activó esta entrega.
createdAtstringLa hora de creación de la carga útil en formato ISO 8601.
userAgentstringEl user-agent de la solicitud que activó el evento.

Cada familia también incluye la dirección IP de la solicitud que activó el evento, bajo el nombre de campo userIp para eventos de flujo de usuario y ip para eventos de mutación de datos y excepciones. La semántica es idéntica; la diferencia de nombre histórica se conserva para compatibilidad con versiones anteriores.

Cargas útiles de eventos de flujo de usuario

Eventos: PostRegister, PostSignIn, PostResetPassword.

Se activan cuando un usuario completa un flujo de registro, inicio de sesión o restablecimiento de contraseña manejado por la Experience API. Además de los campos comunes, el cuerpo lleva:

CampoTipoOpcionalNotas
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'El tipo de evento de flujo de usuario. Se mapea a PostSignIn / PostRegister / PostResetPassword respectivamente. El nombre del campo conserva el nombre histórico "interaction".
sessionIdstringEl ID de la sesión (no el ID de interacción) para este evento, si corresponde.
userIpstringLa dirección IP de la solicitud que activó el evento.
userIdstringEl ID de usuario asociado con este evento, si corresponde.
userUserEntityLa entidad de usuario asociada con este evento, si corresponde.
applicationIdstringEl ID de la aplicación asociado con este evento, si corresponde.
applicationApplicationEntityLa entidad de la aplicación asociada con este evento, si corresponde.

Formas de las entidades

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;
};

Consulta Usuarios y Aplicaciones para la referencia completa de campos.

Cargas útiles de eventos de mutación de datos

Eventos: cada evento bajo User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.* — consulta Eventos de Webhooks → Eventos de mutación de datos para el catálogo completo.

El cuerpo siempre lleva:

Campos de contexto de la Experience API

Presentes cuando el cambio fue activado por un flujo orientado al usuario en la Experience API — por ejemplo, User.Created durante el registro o User.Data.Updated durante las actualizaciones de perfil.

CampoTipoOpcionalNotas
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'El tipo de evento de flujo de usuario que produjo el cambio. El nombre del campo conserva el nombre histórico "interaction".
sessionIdstringEl ID de la sesión (no el ID de interacción) para este evento, si corresponde.
applicationIdstringEl ID de la aplicación, si corresponde.
applicationApplicationEntityLa entidad de la aplicación, si corresponde.

Campos de contexto de la Management API

Presentes cuando el cambio fue activado por una llamada a la Management API.

CampoTipoOpcionalNotas
pathstringLa ruta de la llamada a la API que activó este hook.
methodstringEl método HTTP de la llamada a la API.
statusnumberEl código de estado de respuesta de la llamada a la API.
paramsobjectLos parámetros de ruta de koa de la llamada a la API.
matchedRoutestringLa ruta coincidente de koa. Logto usa este campo para coincidir con los filtros de eventos de webhook habilitados.

Cargas útiles de datos específicas del evento

Cada evento de mutación de datos incluye un campo data de nivel superior que lleva la entidad afectada, o null cuando el cambio no se puede resumir como una sola entidad (eventos de eliminación y membresía). Algunos eventos también incluyen campos de nivel superior específicos del evento más allá de dataOrganization.Membership.Updated es uno de esos casos, documentado a continuación.

Eventos de usuario

EventoCampoTipoOpcionalNotas
User.CreateddataUserEntityLa entidad de usuario creada.
User.Data.UpdateddataUserEntityLa entidad de usuario actualizada.
User.Deleteddatanull/

Eventos de rol

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;
};
EventoCampoTipoOpcionalNotas
Role.CreateddataRoleLa entidad de rol creada.
Role.Data.UpdateddataRoleLa entidad de rol actualizada.
Role.Deleteddatanull/
Role.Scopes.UpdateddataScope[]Los alcances actualizados asignados al rol.
Role.Scopes.UpdatedroleIdstringEl ID del rol al que se asignan los alcances. (Solo disponible cuando el evento fue activado al crear un rol con alcances preasignados.)

Eventos de permiso (alcance)

EventoCampoTipoOpcionalNotas
Scope.CreateddataScopeLa entidad de alcance creada.
Scope.Data.UpdateddataScopeLa entidad de alcance actualizada.
Scope.Deleteddatanull/

Eventos de organización

type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
EventoCampoTipoOpcionalNotas
Organization.CreateddataOrganizationLa entidad de organización creada.
Organization.Data.UpdateddataOrganizationLa entidad de organización actualizada.
Organization.Deleteddatanull/
Organization.Membership.Updateddatanull/El cambio se describe mediante matrices delta opcionales de nivel superior. Ver carga útil de Organization.Membership.Updated a continuación.
Carga útil de Organization.Membership.Updated

Además de los campos comunes y de los campos de contexto de API que correspondan a la fuente del disparador (contexto de Management API para las rutas de la Management API, contexto de Experience API para el aprovisionamiento just-in-time), el evento Organization.Membership.Updated lleva un organizationId más matrices delta opcionales en el nivel superior de la carga útil (junto a event, createdAt, etc.; no dentro de data, que siempre es null para este evento).

CampoTipoOpcionalNotas
organizationIdstringLa organización cuya membresía cambió.
addedUserIdsstring[]IDs de usuario recién agregados por este disparador. Omitido cuando no se agregaron usuarios, o cuando el disparador no afecta la membresía de usuarios.
removedUserIdsstring[]IDs de usuario eliminados por este disparador. Omitido cuando no se eliminaron usuarios.
addedApplicationIdsstring[]IDs de aplicación recién agregados. Omitido cuando no se agregaron aplicaciones, o cuando el disparador no afecta la membresía de aplicaciones.
removedApplicationIdsstring[]IDs de aplicación eliminados. Omitido cuando no se eliminaron aplicaciones.

Las cuatro matrices delta son opcionales y aditivas — no cambian la forma de la carga útil existente para los consumidores que no las esperan, y el campo heredado data: null todavía se emite sin cambios.

Disparadores y qué campos delta pueden emitir
DisparadorCampos delta posibles
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
Aprovisionamiento Just-in-Time al añadir al usuario a una nueva organizaciónaddedUserIds

Nota: el aprovisionamiento just-in-time no emite Organization.Membership.Updated para una organización de la que el usuario ya es miembro.

Las deltas vacías se omiten — ausente ≠ cambio vacío

Las matrices delta vacías se omiten por completo de la carga útil. Por ejemplo, un PUT /organizations/:id/users que reemplaza el conjunto de membresía con el conjunto existente no produce un cambio real, y la carga útil se reduce a solo { organizationId } con los cuatro campos delta ausentes. Lo mismo se aplica a una re-adición de un miembro existente, una re-aceptación de una invitación por parte de un usuario que ya es miembro que ya es miembro.

Los consumidores deben tratar un campo faltante como "sin cambio en ese lado", no como "un cambio vacío".

Límite por matriz (truncamiento silencioso)

Cada matriz delta está limitada a 5000 entradas. Cuando una sola llamada a la Management API agrega o elimina más de 5000 usuarios (o aplicaciones) en una operación, la matriz delta correspondiente se trunca silenciosamente a sus primeras 5000 entradas — no hay un marcador en la carga útil que indique que se activó un límite.

Si tu aplicación realiza operaciones administrativas masivas que pueden afectar plausiblemente a más de 5000 miembros en una llamada, trata una matriz de exactamente 5000 entradas como una señal para reconciliar la membresía autorizada a través de la Management API:

  • GET /organizations/:id/users — membresía completa de usuarios.
  • GET /organizations/:id/applications — membresía completa de aplicaciones.

Esto sigue el mismo patrón que el evento push de GitHub, que limita commits a 20 entradas y señala a los consumidores hacia la API de comparación para la lista completa.

Omitiendo eventos sin cambios

Cada PUT contra las rutas de membresía emite un evento independientemente de si algo realmente cambió — para que cualquier llamada de reemplazo de estado tenga al menos una entrega de webhook para fines de auditoría. Para omitir entregas sin cambios en el lado del consumidor, filtra en la presencia de matrices delta:

if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// cambio real de membresía — manejarlo
}

?.length es falso tanto para undefined como para [], por lo que el mismo predicado es robusto ya sea que el campo esté ausente o (en algún futuro hipotético) se emita como una matriz vacía.

Ejemplos de cargas útiles

Agregar un usuario (POST /organizations/:id/users):

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

Reemplazar el conjunto de membresía de usuarios (PUT /organizations/:id/users):

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

Eliminar un usuario (DELETE /organizations/:id/users/:userId):

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

Agregar una aplicación (POST /organizations/:id/applications):

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

Re-agregar un miembro existente, PUT sin cambios, o re-aceptación de una invitación ya miembro (sin cambio real):

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

Operación masiva que alcanza el límite de 5000 (truncada silenciosamente):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_0001", "u_0002", "/* … exactamente 5000 entradas en total */"]
}

Ver una matriz de exactamente 5000 entradas debería provocar una reconciliación GET /organizations/:id/users (o /applications).

Eventos de rol de organización

type OrganizationRole = {
id: string;
name: string;
description?: string;
};
type OrganizationScope = {
id: string;
name: string;
description?: string;
};
EventoCampoTipoOpcionalNotas
OrganizationRole.CreateddataOrganizationRoleLa entidad de rol de organización creada.
OrganizationRole.Data.UpdateddataOrganizationRoleLa entidad de rol de organización actualizada.
OrganizationRole.Deleteddatanull/
OrganizationRole.Scopes.Updateddatanull/
OrganizationRole.Scopes.UpdatedorganizationRoleIdstringEl ID del rol al que se asignan los alcances. (Solo disponible cuando el evento fue activado al crear un rol con alcances preasignados.)

Eventos de permiso de organización (alcance)

EventoCampoTipoOpcionalNotas
OrganizationScope.CreateddataOrganizationScopeLa entidad de alcance de organización creada.
OrganizationScope.Data.UpdateddataOrganizationScopeLa entidad de alcance de organización actualizada.
OrganizationScope.Deleteddatanull/

Cargas útiles de eventos de excepción

Eventos: Identifier.Lockout.

Se activan en incidentes de seguridad, por ejemplo, una cuenta bloqueada después de intentos fallidos consecutivos de verificación. Estos eventos siempre se originan en un flujo orientado al usuario, por lo que el cuerpo lleva:

enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
CampoTipoOpcionalNotas
typeSignInIdentifierEl tipo de identificador del usuario, por ejemplo, correo electrónico, teléfono o nombre de usuario.
valuestringEl valor del identificador del usuario que activó el bloqueo.