跳至主要內容

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 傳送都會包含:

欄位型別選填說明
hookIdstringLogto 中的 webhook 設定識別碼。
eventstring觸發本次傳送的事件。
createdAtstringPayload 建立時間,ISO 8601 格式。
userAgentstring✅觸發請求的 user-agent。

每個類別也會包含觸發請求的 IP 位址:使用者流程事件欄位名為 userIp,資料變更與例外事件欄位名為 ip。語意相同,僅為相容歷史命名。

使用者流程事件 payload​

事件: PostRegister、PostSignIn、PostResetPassword。

當使用者完成由 Experience API 處理的註冊、登入或重設密碼流程時觸發。除了共用欄位外,主體還包含:

欄位型別選填說明
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'使用者流程事件型別。分別對應 PostSignIn / PostRegister / PostResetPassword。欄位名保留歷史命名。
sessionIdstring✅本事件的 Session ID(非 Interaction ID),如適用。
userIpstring✅觸發請求的 IP 位址。
userIdstring✅本事件關聯的使用者 ID,如適用。
userUserEntity✅本事件關聯的使用者實體,如適用。
applicationIdstring✅本事件關聯的應用程式 ID,如適用。
applicationApplicationEntity✅本事件關聯的應用程式實體,如適用。

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 事件。

主體內容包含:

Experience API 上下文欄位​

當變更由 Experience API 的使用者流程觸發時(如註冊時的 User.Created 或個人資料更新時的 User.Data.Updated),會包含:

欄位型別選填說明
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'✅產生變更的使用者流程事件型別。欄位名保留歷史命名。
sessionIdstring✅本事件的 Session ID(非 Interaction ID),如適用。
applicationIdstring✅應用程式 ID,如適用。
applicationApplicationEntity✅應用程式實體,如適用。

Management API 上下文欄位​

當變更由 Management API 呼叫觸發時會包含:

欄位型別選填說明
pathstring✅觸發本 webhook 的 API 呼叫路徑。
methodstring✅API 呼叫的 HTTP 方法。
statusnumber✅API 呼叫的回應狀態碼。
paramsobject✅API 呼叫的 koa path params。
matchedRoutestring✅koa 匹配到的路由。Logto 用於 webhook 事件篩選。

事件專屬資料 payload​

每個資料變更事件都包含頂層 data 欄位,攜帶受影響的實體,若無法以單一實體摘要(如刪除與成員變更事件)則為 null。部分事件還有額外頂層欄位,Organization.Membership.Updated 為一例,詳見下文。

使用者事件​

事件欄位型別選填說明
User.CreateddataUserEntity新建立的使用者實體。
User.Data.UpdateddataUserEntity更新後的使用者實體。
User.Deleteddatanull/

角色事件​

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✅權限範圍所屬角色 ID。(僅在建立角色時預先指派權限範圍時提供)

權限 (Scope) 事件​

事件欄位型別選填說明
Scope.CreateddataScope新建立的權限範圍實體。
Scope.Data.UpdateddataScope更新後的權限範圍實體。
Scope.Deleteddatanull/

組織事件​

type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
事件欄位型別選填說明
Organization.CreateddataOrganization新建立的組織實體。
Organization.Data.UpdateddataOrganization更新後的組織實體。
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 內,該欄位永遠為 null)攜帶 organizationId 及可選的 delta 陣列。

欄位型別選填說明
organizationIdstring變更成員的組織 ID。
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 與 [] 皆為 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.CreateddataOrganizationRole新建立的組織角色實體。
OrganizationRole.Data.UpdateddataOrganizationRole更新後的組織角色實體。
OrganizationRole.Deleteddatanull/
OrganizationRole.Scopes.Updateddatanull/
OrganizationRole.Scopes.UpdatedorganizationRoleIdstring✅權限範圍所屬角色 ID。(僅在建立角色時預先指派權限範圍時提供)

組織權限(scope)事件​

事件欄位型別選填說明
OrganizationScope.CreateddataOrganizationScope新建立的組織權限範圍實體。
OrganizationScope.Data.UpdateddataOrganizationScope更新後的組織權限範圍實體。
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觸發發送速率限制的 email 或手機號碼。

Grant.LimitExceeded​

當成功授權導致使用者超過應用程式的 最大同時驗證裝置數(maxAllowedGrants)時觸發,Logto 會撤銷該應用程式最舊的授權。

此事件由 OIDC 授權端點發出,不包含 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 之後無法再透過授權查詢端點或 Console 查到(僅回傳有效授權)。如需留存,請自行保存這些 ID。
  • 僅當實際有授權被撤銷時才會觸發事件。若授權未超過上限則不會產生事件。
  • 每次授權超過上限都會觸發一次事件,因此使用者若不斷從超過允許數量的裝置登入,每次都會有一筆撤銷事件。
  • 傳送採 fire-and-forget:慢速或失敗的端點不會阻擋或影響使用者授權。失敗的傳送會如其他 webhook 一樣記錄於稽核日誌。