跳到主要内容

Webhooks 请求

当一个 webhook 事件被触发时,Logto 会向每个已订阅该事件的端点发送一个 POST 请求。完整的事件目录见 Webhooks 事件;本页记录了 Logto 发送的请求结构。

请求头​

Key可自定义说明
user-agent✅默认值为 Logto (https://logto.io/)。
content-type✅默认值为 application/json。
logto-signature-sha-256请求体的签名。详见 保护你的 webhooks。

可自定义的请求头可以通过 安全 webhook 配置进行覆盖。

请求体概览​

请求体是一个 JSON 对象。其具体结构取决于事件所属的类别:

类别事件触发时机
用户流程PostRegister, PostSignIn, PostResetPassword用户完成由体验 (Experience) API 处理的注册、登录或重置密码流程时触发。
数据变更User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*通过 Management API 调用或体验 (Experience) API 上的用户流程导致底层数据模型发生变更时触发。
异常Identifier.Lockout, Message.RateLimited, Grant.LimitExceeded安全事件,例如连续验证失败后账户被锁定等。

每个类别都包含一组通用字段。每个类别还会叠加自身的请求上下文字段和事件特定的 payload。

通用字段​

无论属于哪个类别,每次投递都会包含:

字段类型可选说明
hookIdstringLogto 中 webhook 配置的标识符。
eventstring触发本次投递的事件。
createdAtstring以 ISO 8601 格式表示的 payload 创建时间。
userAgentstring✅触发请求的 user-agent。

每个类别还会包含触发请求的 IP 地址:用户流程事件下字段名为 userIp,数据变更和异常事件下为 ip。语义一致,仅为历史兼容保留不同命名。

用户流程事件 payload​

事件: PostRegister, PostSignIn, PostResetPassword。

当用户完成由体验 (Experience) API 处理的注册、登录或重置密码流程时触发。除了通用字段外,请求体还包含:

字段类型可选说明
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'用户流程事件类型。分别对应 PostSignIn / PostRegister / PostResetPassword。字段名保留历史 "interaction" 命名。
sessionIdstring✅本事件的 Session ID(非 Interaction ID),如适用。
userIpstring✅触发请求的 IP 地址。
userIdstring✅与本事件关联的用户 ID,如适用。
userUserEntity✅与本事件关联的用户实体,如适用。
applicationIdstring✅与本事件关联的应用 ID,如适用。
applicationApplicationEntity✅与本事件关联的应用实体,如适用。

实体结构​

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

完整字段参考见 用户 和 应用。

数据变更事件 payload​

事件: 所有 User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.* 下的事件。完整目录见 Webhooks 事件 → 数据变更 webhook 事件。

请求体始终包含:

  • 通用字段。
  • 一个 ip 字段,表示触发请求的 IP 地址(可选,已知时提供)。
  • 一个API 上下文,描述变更的触发方式。上下文根据触发来源有两种变体:
  • 一个事件特定 payload:受影响的实体在 data 字段中(部分事件还会有额外顶层字段)。详见事件特定数据 payload。

体验 (Experience) API 上下文字段​

当变更由体验 (Experience) API 上的用户端流程触发时出现,例如注册时的 User.Created 或资料更新时的 User.Data.Updated。

字段类型可选说明
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'✅产生变更的用户流程事件类型。字段名保留历史 "interaction" 命名。
sessionIdstring✅本事件的 Session ID(非 Interaction ID),如适用。
applicationIdstring✅应用 ID,如适用。
applicationApplicationEntity✅应用实体,如适用。

Management API 上下文字段​

当变更由 Management API 调用触发时出现。

字段类型可选说明
pathstring✅触发本 webhook 的 API 调用路径。
methodstring✅API 调用的 HTTP 方法。
statusnumber✅API 调用的响应状态码。
paramsobject✅API 调用的 koa 路径参数。
matchedRoutestring✅koa 匹配到的路由。Logto 用于匹配已启用的 webhook 事件过滤器。

事件特定数据 payload​

每个数据变更事件都包含顶层的 data 字段,携带受影响的实体;如果变更无法归纳为单一实体(如删除和成员变更事件),则为 null。部分事件还会有除 data 外的事件特定顶层字段,Organization.Membership.Updated 就是其中之一,见下文。

用户事件​

事件字段类型可选说明
User.CreateddataUserEntity新创建的用户实体。
User.Data.UpdateddataUserEntity更新后的用户实体。
User.Deleteddatanull/

角色 (Role) 事件​

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;
};
事件字段类型可选说明
Role.CreateddataRole新创建的角色实体。
Role.Data.UpdateddataRole更新后的角色实体。
Role.Deleteddatanull/
Role.Scopes.UpdateddataScope[]分配给该角色的更新后权限 (Scopes)。
Role.Scopes.UpdatedroleIdstring✅分配权限 (Scopes) 的角色 ID。(仅在通过预分配权限创建角色时提供。)

权限 (Scope) 事件​

事件字段类型可选说明
Scope.CreateddataScope新创建的权限 (Scope) 实体。
Scope.Data.UpdateddataScope更新后的权限 (Scope) 实体。
Scope.Deleteddatanull/

组织 (Organization) 事件​

type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
事件字段类型可选说明
Organization.CreateddataOrganization新创建的组织 (Organization) 实体。
Organization.Data.UpdateddataOrganization更新后的组织 (Organization) 实体。
Organization.Deleteddatanull/
Organization.Membership.Updateddatanull/变更通过可选顶层 delta 数组描述。见下文 Organization.Membership.Updated payload。
Organization.Membership.Updated payload​

除了通用字段和适用于触发来源的 API 上下文字段(Management API 路由用 Management API 上下文,即时供应用 体验 (Experience) API 上下文),Organization.Membership.Updated 事件还会在 payload 顶层(与 event、createdAt 等并列,不在 data 内,该事件下 data 始终为 null)携带 organizationId 及可选 delta 数组。

字段类型可选说明
organizationIdstring发生成员变更的组织 (Organization)。
addedUserIdsstring[]✅本次触发新增的用户 ID。未新增用户或不影响用户成员时省略。
removedUserIdsstring[]✅本次触发移除的用户 ID。未移除用户时省略。
addedApplicationIdsstring[]✅新增的应用 ID。未新增应用或不影响应用成员时省略。
removedApplicationIdsstring[]✅移除的应用 ID。未移除应用时省略。

这四个 delta 数组可选且增量:对于不关心它们的消费方不会改变现有 payload 结构,且历史的 data: null 字段依然保留。

触发方式与可能出现的 delta 字段​
触发方式可能出现的 delta 字段
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
即时供应将用户加入新组织时addedUserIds
空 delta 会被省略(缺失 ≠ 空变更)​

空的 delta 数组会完全省略在 payload 中。例如,PUT /organizations/:id/users 用现有成员集替换成员集时没有实际变更,payload 只剩 { organizationId },四个 delta 字段都缺失。重新添加已存在成员、已是成员的用户再次接受邀请也同理。

消费方必须将缺失字段视为“该侧无变更”,而不是“空变更”。

单数组上限(静默截断)​

每个 delta 数组上限为5000 条。当一次 Management API 调用在单次操作中新增或移除超过 5000 个用户(或应用)时,相应的 delta 数组会被静默截断为前 5000 条。payload 内不会有任何标记说明被截断。

如果你的应用会进行可能影响超过 5000 个成员的批量管理操作,看到数组正好为 5000 条时应主动通过 Management API 对成员进行权威同步:

  • GET /organizations/:id/users:获取完整用户成员列表。
  • GET /organizations/:id/applications:获取完整应用成员列表。

这与 GitHub 的 push 事件类似,commits 上限 20 条,完整列表需用 compare API 获取。

跳过无操作事件​

要在消费方跳过无操作(无 delta 字段)投递,可按 delta 数组是否存在过滤:

if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// 有实际成员变更,处理之
}

?.length 对 undefined 和 [] 都为假,因此无论字段缺失还是(假设未来)发空数组都能兼容。

示例 payload​

添加用户(POST /organizations/:id/users):

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

替换用户成员集(PUT /organizations/:id/users):

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

移除用户(DELETE /organizations/:id/users/:userId):

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

添加应用(POST /organizations/:id/applications):

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

重新添加已存在成员、无操作 PUT 或已是成员的邀请再次接受(无实际变更):

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

批量操作触发 5000 上限(静默截断):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_0001", "u_0002", "/* … 共 5000 条 */"]
}

看到数组正好 5000 条时应主动发起 GET /organizations/:id/users(或 /applications)同步。

组织角色事件​

type OrganizationRole = {
id: string;
name: string;
description?: string;
};
type OrganizationScope = {
id: string;
name: string;
description?: string;
};
事件字段类型可选说明
OrganizationRole.CreateddataOrganizationRole新创建的组织角色实体。
OrganizationRole.Data.UpdateddataOrganizationRole更新后的组织角色实体。
OrganizationRole.Deleteddatanull/
OrganizationRole.Scopes.Updateddatanull/
OrganizationRole.Scopes.UpdatedorganizationRoleIdstring✅分配权限 (Scopes) 的角色 ID。(仅在通过预分配权限创建角色时提供。)

组织权限 (scope) 事件​

事件字段类型可选说明
OrganizationScope.CreateddataOrganizationScope新创建的组织权限 (scope) 实体。
OrganizationScope.Data.UpdateddataOrganizationScope更新后的组织权限 (scope) 实体。
OrganizationScope.Deleteddatanull/

异常事件 payload​

事件: Identifier.Lockout, Message.RateLimited, Grant.LimitExceeded。

在安全事件发生时触发,例如连续验证失败后账户被锁定,或因应用并发设备数超限而撤销授权。

每个异常事件都包含通用字段和一个 ip 字段(结构同数据变更事件)。其余字段依事件而异。

Identifier.Lockout​

来源于用户端流程,因此请求体还包含体验 (Experience) API 上下文字段,以及:

enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
字段类型可选说明
typeSignInIdentifier用户的标识类型,如 email、phone 或 username。
valuestring触发锁定的用户标识值。

Message.RateLimited​

来源于用户端流程,因此请求体还包含体验 (Experience) API 上下文字段,以及:

字段类型可选说明
actionstring被限流的操作,例如 VerificationCodeSend。
recipientstring触发发送速率限制的邮箱地址或手机号。

Grant.LimitExceeded​

当一次成功授权使用户超出应用的最大并发认证设备数限制(maxAllowedGrants)并导致 Logto 撤销其最早的授权时触发。

该事件由 OIDC 授权端点发出,而非体验 (Experience) API,因此不包含 interactionEvent 或 sessionId。除通用字段和 ip 外,请求体还包含:

字段类型可选说明
userIdstring被撤销授权的用户。
applicationIdstring超出 maxAllowedGrants 限制的应用。
applicationApplicationEntity✅应用实体。若投递时无法解析应用则省略。
maxAllowedGrantsnumber事件触发时应用配置的限制值。
preRevocationActiveGrantCountnumber撤销前用户在该应用下持有的活跃授权数(含本次新发放的)。
revokedGrantIdsstring[]实际被撤销的授权 ID,按最早顺序排列。

示例 payload:

{
"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"]
}

投递说明:

  • 被撤销的授权记录会在撤销时被销毁,因此 revokedGrantIds 中的 ID 无法再通过授权列表接口或控制台查询——这些接口只返回活跃授权。如需后续使用,请自行持久化这些 ID。
  • 仅当实际有授权被撤销时才会触发该事件。未超限的授权不会产生事件。
  • 每次授权超限都会触发一次事件,因此用户多设备重复登录会产生多次撤销事件。
  • 投递为“即发即弃”:慢速或失败的端点不会阻塞或导致用户授权失败。失败的投递会像其他 webhook 一样记录在审计日志中。