Webhook 請求 (Webhooks request)
當 webhook 事件觸發時,Logto 會向所有訂閱該事件的端點發送 POST 請求。完整事件目錄請參閱 Webhook 事件;本頁說明 Logto 傳送的請求格式。
請求標頭
| Key | 可自訂 | 說明 |
|---|---|---|
| user-agent | ✅ | 預設為 Logto (https://logto.io/)。 |
| content-type | ✅ | 預設為 application/json。 |
| logto-signature-sha-256 | 請求主體的簽章。詳見 保護你的 webhook。 |
可自訂標頭可透過 安全 webhook 設定覆寫。
請求主體概覽
主體為 JSON 物件。其具體格式取決於事件所屬類別:
| 類別 | 事件 | 觸發時機 |
|---|---|---|
| 使用者流程 | PostRegister、PostSignIn、PostResetPassword | 使用者完成由 Experience API 處理的註冊、登入或重設密碼流程時。 |
| 資料變更 | User.*、Role.*、Scope.*、Organization.*、OrganizationRole.*、OrganizationScope.* | 透過 Management API 呼叫或 Experience API 使用者流程導致底層資料模型變更時。 |
| 例外 | Identifier.Lockout、Message.RateLimited、Grant.LimitExceeded | 安全事件,例如連續驗證失敗導致帳號鎖定等。 |
每個類別都包含一組共用欄位。各類別會再加上自身的請求上下文欄位及事件專屬 payload。
共用欄位
無論類別,所有 webhook 傳送都會包含:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| hookId | string | Logto 中的 webhook 設定識別碼。 | |
| event | string | 觸發本次傳送的事件。 | |
| createdAt | string | Payload 建立時間,ISO 8601 格式。 | |
| userAgent | string | ✅ | 觸發請求的 user-agent。 |
每個類別也會包含觸發請求的 IP 位址:使用者流程事件欄位名為 userIp,資料變更與例外事件欄位名為 ip。語意相同,僅為相容歷史命名。
使用者流程事件 payload
事件: PostRegister、PostSignIn、PostResetPassword。
當使用者完成由 Experience API 處理的註冊、登入或重設密碼流程時觸發。除了共用欄位外,主體還包含:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | 使用者流程事件型別。分別對應 PostSignIn / PostRegister / PostResetPassword。欄位名保留歷史命名。 | |
| sessionId | string | ✅ | 本事件的 Session ID(非 Interaction ID),如適用。 |
| userIp | string | ✅ | 觸發請求的 IP 位址。 |
| userId | string | ✅ | 本事件關聯的使用者 ID,如適用。 |
| user | UserEntity | ✅ | 本事件關聯的使用者實體,如適用。 |
| applicationId | string | ✅ | 本事件關聯的應用程式 ID,如適用。 |
| application | ApplicationEntity | ✅ | 本事件關聯的應用程式實體,如適用。 |
Entity 格式
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.* 事件。完整目錄請見 Webhook 事件 → 資料變更 webhook 事件。
主體內容包含:
- 共用欄位。
ip欄位,觸發請求的 IP 位址(選填,已知時提供)。- API 上下文,描述變更來源。根據觸發來源有兩種變體:
- Experience API 上下文:來自使用者流程。
- Management API 上下文:來自直接 Management API 呼叫。
- 事件專屬 payload:受影響的實體於
data欄位,部分事件有額外頂層欄位。詳見 事件專屬資料 payload。
Experience API 上下文欄位
當變更由 Experience API 的使用者流程觸發時(如註冊時的 User.Created 或個人資料更新時的 User.Data.Updated),會包含:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | ✅ | 產生變更的使用者流程事件型別。欄位名保留歷史命名。 |
| sessionId | string | ✅ | 本事件的 Session ID(非 Interaction ID),如適用。 |
| applicationId | string | ✅ | 應用程式 ID,如適用。 |
| application | ApplicationEntity | ✅ | 應用程式實體,如適用。 |
Management API 上下文欄位
當變更由 Management API 呼叫觸發時會包含:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| path | string | ✅ | 觸發本 webhook 的 API 呼叫路徑。 |
| method | string | ✅ | API 呼叫的 HTTP 方法。 |
| status | number | ✅ | API 呼叫的回應狀態碼。 |
| params | object | ✅ | API 呼叫的 koa path params。 |
| matchedRoute | string | ✅ | koa 匹配到的路由。Logto 用於 webhook 事件篩選。 |
事件專屬資料 payload
每個資料變更事件都包含頂層 data 欄位,攜帶受影響的實體,若無法以單一實體摘要(如刪除與成員變更事件)則為 null。部分事件還有額外頂層欄位,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[] | 指派給該角色的更新後權限範圍 (scopes)。 | |
| Role.Scopes.Updated | roleId | string | ✅ | 權限範圍所屬角色 ID。(僅在建立角色時預先指派權限範圍時提供) |
權限 (Scope) 事件
| 事件 | 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|---|
| 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 | / | 變更內容由頂層 delta 陣列描述,詳見 Organization.Membership.Updated payload。 |
Organization.Membership.Updated payload
除了共用欄位與適用的 API 上下文欄位(Management API 路由用 Management API 上下文,即時佈建用 Experience API 上下文),Organization.Membership.Updated 事件會在 payload 頂層(與 event、createdAt 等同層,不在 data 內,該欄位永遠為 null)攜帶 organizationId 及可選的 delta 陣列。
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| organizationId | string | 變更成員的組織 ID。 | |
| addedUserIds | string[] | ✅ | 本次觸發新增的使用者 ID。若無新增或不影響使用者成員則省略。 |
| removedUserIds | string[] | ✅ | 本次觸發移除的使用者 ID。若無移除則省略。 |
| addedApplicationIds | string[] | ✅ | 本次觸發新增的應用程式 ID。若無新增或不影響應用程式成員則省略。 |
| removedApplicationIds | string[] | ✅ | 本次觸發移除的應用程式 ID。若無移除則省略。 |
這四個 delta 陣列可選且具加值性:對於不預期這些欄位的消費端,既有 payload 格式不變,且舊有 data: null 欄位仍會保留。
觸發來源與可能出現的 delta 欄位
| 觸發來源 | 可能出現的 delta 欄位 |
|---|---|
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 |
| 即時佈建新增使用者至新組織 | 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 與 [] 皆為 falsy,因此此判斷式無論欄位缺席或(假設未來)為空陣列皆適用。
範例 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.Created | data | OrganizationRole | 新建立的組織角色實體。 | |
| OrganizationRole.Data.Updated | data | OrganizationRole | 更新後的組織角色實體。 | |
| OrganizationRole.Deleted | data | null | / | |
| OrganizationRole.Scopes.Updated | data | null | / | |
| OrganizationRole.Scopes.Updated | organizationRoleId | string | ✅ | 權限範圍所屬角色 ID。(僅在建立角色時預先指派權限範圍時提供) |
組織權限(scope)事件
| 事件 | 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|---|
| OrganizationScope.Created | data | OrganizationScope | 新建立的組織權限範圍實體。 | |
| OrganizationScope.Data.Updated | data | OrganizationScope | 更新後的組織權限範圍實體。 | |
| OrganizationScope.Deleted | data | null | / |
例外事件 payload
事件: Identifier.Lockout、Message.RateLimited、Grant.LimitExceeded。
於安全事件時觸發,例如連續驗證失敗導致帳號鎖定,或因應用程式同時登入裝置數超限而撤銷授權。
每個例外事件都包含共用欄位及 ip 欄位(格式同資料變更事件)。其餘欄位依事件而異。
Identifier.Lockout
來源為使用者流程,因此主體也包含 Experience API 上下文欄位,並加上:
enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| type | SignInIdentifier | 使用者識別型別,如 email、phone 或 username。 | |
| value | string | 觸發鎖定的使用者識別值。 |
Message.RateLimited
來源為使用者流程,因此主體也包含 Experience API 上下文欄位,並加上:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| action | string | 被限流的動作,例如 VerificationCodeSend。 | |
| recipient | string | 觸發發送速率限制的 email 或手機號碼。 |
Grant.LimitExceeded
當成功授權導致使用者超過應用程式的 最大同時驗證裝置數(maxAllowedGrants)時觸發,Logto 會撤銷該應用程式最舊的授權。
此事件由 OIDC 授權端點發出,不包含 interactionEvent 或 sessionId。除共用欄位與 ip 外,主體還包含:
| 欄位 | 型別 | 選填 | 說明 |
|---|---|---|---|
| userId | string | 被撤銷授權的使用者。 | |
| applicationId | string | 超過 maxAllowedGrants 限制的應用程式。 | |
| application | ApplicationEntity | ✅ | 應用程式實體。若無法於傳送時解析則省略。 |
| maxAllowedGrants | number | 事件觸發時應用程式設定的上限。 | |
| preRevocationActiveGrantCount | number | 撤銷前該使用者於此應用程式持有的有效授權數(含本次新發放者)。 | |
| revokedGrantIds | string[] | 實際被撤銷的授權 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 之後無法再透過授權查詢端點或 Console 查到(僅回傳有效授權)。如需留存,請自行保存這些 ID。 - 僅當實際有授權被撤銷時才會觸發事件。若授權未超過上限則不會產生事件。
- 每次授權超過上限都會觸發一次事件,因此使用者若不斷從超過允許數量的裝置登入,每次都會有一筆撤銷事件。
- 傳送採 fire-and-forget:慢速或失敗的端點不會阻擋或影響使用者授權。失敗的傳送會如其他 webhook 一樣記錄於稽核日誌。