Webhooks 請求
當 webhook 事件觸發時,Logto 會向每個訂閱的端點發送一個 POST 請求。完整的事件目錄位於 Webhooks 事件;本頁記錄了 Logto 傳送的請求結構。
請求標頭
| Key | 可自訂 | 說明 |
|---|---|---|
| user-agent | ✅ | 預設為 Logto (https://logto.io/)。 |
| content-type | ✅ | 預設為 application/json。 |
| logto-signature-sha-256 | 請求主體的簽名。請參閱 保護你的 webhooks。 |
可自訂的標頭可以透過 secure webhook 配置覆蓋。
請求主體概述
主體是一個 JSON 物件。其具體結構取決於事件所屬的類別:
| 類別 | 事件 | 觸發時機 |
|---|---|---|
| 使用者流程 | PostRegister、PostSignIn、PostResetPassword | 使用者完成由 Experience API 處理的註冊、登入或重設密碼流程。 |
| 資料變更 | User.*、Role.*、Scope.*、Organization.*、OrganizationRole.*、OrganizationScope.* | 基礎資料模型被變更 — 透過 Management API 呼叫或 Experience API 上的使用者流程。 |
| 例外 | Identifier.Lockout | 安全事件 — 例如,帳戶在連續驗證失敗後被鎖定。 |
每個類別共享一小組通用欄位。然後每個類別會添加其自己的請求上下文欄位以及特定事件的負載。
通用欄位
無論類別如何,每次傳送都會包含:
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| hookId | string | Logto 中的 webhook 配置識別符。 | |
| event | string | 觸發此傳送的事件。 | |
| createdAt | string | 負載創建時間,格式為 ISO 8601。 | |
| userAgent | string | ✅ | 觸發請求的 user-agent。 |
每個類別還包括觸發請求的 IP 地址 — 在使用者流程事件中為 userIp 欄位,在資料變更和例外事件中為 ip 欄位。語義相同;歷史名稱差異是為了向後相容。
使用者流程事件負載
事件: PostRegister、PostSignIn、PostResetPassword。
當使用者完成由 Experience API 處理的註冊、登入或重設密碼流程時觸發。除了通用欄位外,主體還包含:
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | 使用者流程事件類型。分別對應 PostSignIn / PostRegister / PostResetPassword。欄位名稱保留歷史上的「interaction」命名。 | |
| sessionId | string | ✅ | 此事件的 Session ID(非 Interaction ID),如果適用。 |
| userIp | string | ✅ | 觸發請求的 IP 地址。 |
| userId | string | ✅ | 與此事件相關的使用者 ID,如果適用。 |
| user | UserEntity | ✅ | 與此事件相關的使用者實體,如果適用。 |
| applicationId | string | ✅ | 與此事件相關的應用程式 ID,如果適用。 |
| application | ApplicationEntity | ✅ | 與此事件相關的應用程式實體,如果適用。 |
實體結構
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;
};
資料變更事件負載
事件: User.*、Role.*、Scope.*、Organization.*、OrganizationRole.*、OrganizationScope.* 下的每個事件 — 請參閱 Webhooks 事件 → 資料變更 hook 事件 以獲取完整目錄。
主體始終包含:
- 通用欄位。
- 一個
ip欄位 — 觸發請求的 IP 地址(可選,已知時存在)。 - 描述變更如何觸發的 API 上下文。根據觸發來源,上下文有兩種變體之一:
- Experience API 上下文 — 當變更來自使用者面向的流程時。
- Management API 上下文 — 當變更來自直接的 Management API 呼叫時。
- 事件特定負載 — 在
data中的受影響實體,以及(對於某些事件)其他頂層欄位。請參閱 事件特定資料負載。
Experience API 上下文欄位
當變更由 Experience API 上的使用者面向流程觸發時存在 — 例如,註冊期間的 User.Created 或個人資料更新期間的 User.Data.Updated。
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | ✅ | 產生變更的使用者流程事件類型。欄位名稱保留歷史上的「interaction」命名。 |
| sessionId | string | ✅ | 此事件的 Session ID(非 Interaction ID),如果適用。 |
| applicationId | string | ✅ | 應用程式 ID,如果適用。 |
| application | ApplicationEntity | ✅ | 應用程式實體,如果適用。 |
Management API 上下文欄位
當變更由 Management API 呼叫觸發時存在。
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| path | string | ✅ | 觸發此 hook 的 API 呼叫路徑。 |
| method | string | ✅ | API 呼叫的 HTTP 方法。 |
| status | number | ✅ | API 呼叫的回應狀態碼。 |
| params | object | ✅ | API 呼叫的 koa 路徑參數。 |
| matchedRoute | string | ✅ | koa 匹配的路由。Logto 使用此欄位來匹配啟用的 webhook 事件過濾器。 |
事件特定資料負載
每個資料變更事件都包含一個頂層的 data 欄位,攜帶受影響的實體,或在變更無法總結為單一實體時(刪除和成員事件)為 null。某些事件還包括 data 之外的事件特定頂層欄位 — Organization.Membership.Updated 就是這樣一個例子,詳見下文。
使用者事件
| 事件 | 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|---|
| User.Created | data | UserEntity | 創建的使用者實體。 | |
| User.Data.Updated | data | UserEntity | 更新的使用者實體。 | |
| User.Deleted | data | null | / |
角色事件
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.Created | data | Role | 創建的角色實體。 | |
| Role.Data.Updated | data | Role | 更新的角色實體。 | |
| Role.Deleted | data | null | / | |
| Role.Scopes.Updated | data | Scope[] | 分配給角色的更新權限範圍。 | |
| Role.Scopes.Updated | roleId | string | ✅ | 分配權限範圍的角色 ID。(僅在事件由創建具有預先分配權限範圍的角色觸發時可用。) |
權限(範圍)事件
| 事件 | 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|---|
| Scope.Created | data | Scope | 創建的權限範圍實體。 | |
| Scope.Data.Updated | data | Scope | 更新的權限範圍實體。 | |
| Scope.Deleted | data | null | / |
組織事件
type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
| 事件 | 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|---|
| Organization.Created | data | Organization | 創建的組織實體。 | |
| Organization.Data.Updated | data | Organization | 更新的組織實體。 | |
| Organization.Deleted | data | null | / | |
| Organization.Membership.Updated | data | null | / | 變更由可選的頂層增量陣列描述。請參閱下文的 Organization.Membership.Updated 負載。 |
Organization.Membership.Updated 負載
除了共用欄位以及依觸發來源而定的 API 上下文欄位(Management API 路由對應 Management API 上下文,即時 (JIT) 佈建對應 Experience API 上下文)之外,Organization.Membership.Updated 事件還攜帶一個 organizationId,以及在負載頂層的可選增量陣列(與 event、createdAt 等並列,不在 data 中,對於此事件始終為 null)。
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| organizationId | string | 成員變更的組織。 | |
| addedUserIds | string[] | ✅ | 此觸發新增的使用者 ID。當未新增使用者或觸發不影響使用者成員時省略。 |
| removedUserIds | string[] | ✅ | 此觸發移除的使用者 ID。當未移除使用者時省略。 |
| addedApplicationIds | string[] | ✅ | 新增的應用程式 ID。當未新增應用程式或觸發不影響應用程式成員時省略。 |
| removedApplicationIds | string[] | ✅ | 移除的應用程式 ID。當未移除應用程式時省略。 |
四個增量陣列是可選且附加的 — 它們不會改變對不期望它們的消費者的現有負載結構,並且遺留的 data: null 欄位仍然不變地發出。
觸發器及其可能發出的增量欄位
| 觸發器 | 可能的增量欄位 |
|---|---|
POST /organizations/:id/users | addedUserIds |
PUT /organizations/:id/users | addedUserIds、removedUserIds |
DELETE /organizations/:id/users/:userId | removedUserIds |
POST /organizations/:id/applications | addedApplicationIds |
PUT /organizations/:id/applications | addedApplicationIds、removedApplicationIds |
DELETE /organizations/:id/applications/:applicationId | removedApplicationIds |
PUT /organization-invitations/:id/status (Accepted) | addedUserIds |
| 將使用者新增至新組織時的即時 (JIT) 佈建 | addedUserIds |
空增量被省略 — 缺席 ≠ 空變更
空增量陣列會從負載中完全省略。例如,將成員集替換為現有集的 PUT /organizations/:id/users 不會產生實際變更,負載僅簡化為 { organizationId },所有四個增量欄位均缺席。同樣適用於重新添加現有成員、已是成員的使用者重新接受邀請。
消費者必須將缺失欄位視為「該方面無變更」,而不是「空變更」。
每個陣列的上限(靜默截斷)
每個增量陣列的上限為 5000 個條目。當單個 Management API 呼叫在一次操作中新增或移除超過 5000 個使用者(或應用程式)時,對應的增量陣列會靜默截斷為其前 5000 個條目 — 負載中沒有標記表示上限已觸發。
如果你的應用程式執行的管理批量操作可能在一次呼叫中影響超過 5000 個成員,請將正好 5000 個條目的陣列視為信號,通過 Management API 協調權威成員:
GET /organizations/:id/users— 完整的使用者成員。GET /organizations/:id/applications— 完整的應用程式成員。
這遵循 GitHub 的 push 事件的相同模式,該事件將 commits 限制為 20 個條目,並指示消費者使用比較 API 獲取完整列表。
跳過無操作事件
每次針對成員路由的 PUT 都會發出事件,無論是否實際改變 — 以便任何狀態替換呼叫至少有一個 webhook 傳送以供審計。要在消費者端跳過無操作傳送,請過濾增量陣列的存在:
if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// 實際成員變更 — 處理它
}
?.length 對於 undefined 和 [] 都是偽值,因此無論欄位是缺席還是(在某個假設的未來)作為空陣列發出,相同的謂詞都是穩健的。
範例負載
新增使用者(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.Created | data | OrganizationRole | 創建的組織角色實體。 | |
| OrganizationRole.Data.Updated | data | OrganizationRole | 更新的組織角色實體。 | |
| OrganizationRole.Deleted | data | null | / | |
| OrganizationRole.Scopes.Updated | data | null | / | |
| OrganizationRole.Scopes.Updated | organizationRoleId | string | ✅ | 分配權限範圍的角色 ID。(僅在事件由創建具有預先分配權限範圍的角色觸發時可用。) |
組織權限(範圍)事件
| 事件 | 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|---|
| OrganizationScope.Created | data | OrganizationScope | 創建的組織權限範圍實體。 | |
| OrganizationScope.Data.Updated | data | OrganizationScope | 更新的組織權限範圍實體。 | |
| OrganizationScope.Deleted | data | null | / |
例外事件負載
事件: Identifier.Lockout。
在安全事件中觸發 — 例如,帳戶在連續驗證失敗後被鎖定。這些事件始終源自使用者面向的流程,因此主體攜帶:
- 通用欄位。
ip欄位(與資料變更事件的形狀相同)。- Experience API 上下文欄位。
- 下列例外特定欄位。
enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
| 欄位 | 類型 | 可選 | 備註 |
|---|---|---|---|
| type | SignInIdentifier | 使用者的識別符類型,例如電子郵件、電話或使用者名稱。 | |
| value | string | 觸發鎖定的使用者識別符值。 |