Webhooks リクエスト
Webhook イベントが発火すると、Logto はそれにサブスクライブしているすべてのエンドポイントに POST リクエストを送信します。全イベントカタログは Webhooks イベント にあります。本ページでは 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 | セキュリティインシデント時(例:連続失敗でアカウントロックなど)。 |
すべてのファミリーは 共通フィールド を持ちます。各ファミリーはさらに独自のリクエストコンテキストフィールドとイベント固有のペイロードを追加します。
共通フィールド
ファミリーに関係なくすべての配信に含まれます:
| フィールド | 型 | オプション | 備考 |
|---|---|---|---|
| hookId | string | Logto の Webhook 設定識別子。 | |
| deliveryId | string | この配信の識別子。Webhook・イベントごとに一意で、自動リトライ 時も同じリクエストで保持されます。配信の重複排除 を参照。 | |
| event | string | この配信をトリガーしたイベント。 | |
| createdAt | string | ISO 8601 形式のペイロード作成時刻。 | |
| userAgent | string | ✅ | トリガーリクエストのユーザーエージェント。 |
各ファミリーはまた、トリガーリクエストの IP アドレスも含みます。ユーザーフローイベントでは userIp、データ変更・例外イベントでは ip というフィールド名です。意味は同じですが、後方互換性のため名前の違いが残っています。
配信の重複排除
配信はエンドポイントに複数回届く場合があります:Logto はレスポンスステータスが 500 以上の場合リクエストを リトライ するため、エンドポイントが処理済みでも 5xx を返した場合(例:遅いハンドラの前段ゲートウェイが 504 を返すなど)、再度届きます。同じ配信のすべての試行は同じ deliveryId を持ちますが、新しいイベントや別の Webhook への送信は新しい ID になります。処理済みペイロードの deliveryId を記録し、既知の ID のペイロードはスキップしてください。
deliveryId は配信を記録する監査ログエントリの ID でもあるため、サービスが報告した値は Console の Webhook 最近のリクエストから開いたり、GET /api/logs/:id で取得できます。
テストペイロード も deliveryId を持つため、同じハンドラで処理できますが、テストリクエストはログに記録されません。
ユーザーフローイベントのペイロード
イベント: PostRegister, PostSignIn, PostResetPassword
エンドユーザーが Experience API でサインアップ・サインイン・パスワードリセットを完了したときに発火します。共通フィールド に加え、ボディには以下が含まれます:
| フィールド | 型 | オプション | 備考 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | ユーザーフローイベント種別。PostSignIn / PostRegister / PostResetPassword に対応。フィールド名は歴史的な "interaction" を保持。 | |
| sessionId | string | ✅ | このイベントのセッション 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 イベント → データ変更 Webhook イベント を参照。
ボディには常に以下が含まれます:
- 共通フィールド
ipフィールド(トリガーリクエストの IP アドレス。オプション、取得できた場合のみ)- API コンテキスト(変更がどのようにトリガーされたかを記述)。トリガー元によって 2 種類のいずれか:
- Experience API コンテキスト:ユーザー向けフローからの変更時
- Management API コンテキスト:Management API 直接呼び出し時
- イベント固有のペイロード:
dataに影響を受けたエンティティ(イベントによっては追加のトップレベルフィールドも)。イベント固有データペイロード を参照。
Experience API コンテキストフィールド
Experience API のユーザー向けフローで変更がトリガーされた場合に含まれます。例:サインアップ時の User.Created やプロフィール更新時の User.Data.Updated。
| フィールド | 型 | オプション | 備考 |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | ✅ | 変更を生んだユーザーフローイベント種別。フィールド名は歴史的な "interaction" を保持。 |
| sessionId | string | ✅ | このイベントのセッション 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 パスパラメータ。 |
| 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 | / | 変更内容はオプションのトップレベル delta 配列で記述。Organization.Membership.Updated ペイロード を参照。 |
Organization.Membership.Updated ペイロード
共通フィールド とトリガー元に応じた API コンテキストフィールド(Management API ルートなら Management API コンテキスト、ジャストインタイムプロビジョニングなら Experience API コンテキスト)に加え、Organization.Membership.Updated イベントは organizationId と、ペイロードのトップレベル(event や createdAt などと同階層、data の中ではなく)にオプションの delta 配列を持ちます(このイベントの data は常に null)。
| フィールド | 型 | オプション | 備考 |
|---|---|---|---|
| organizationId | string | メンバーシップが変更された組織。 | |
| addedUserIds | string[] | ✅ | このトリガーで新たに追加されたユーザー ID。追加がなければ省略、またはユーザーメンバーシップに影響しない場合も省略。 |
| removedUserIds | string[] | ✅ | このトリガーで削除されたユーザー ID。削除がなければ省略。 |
| addedApplicationIds | string[] | ✅ | 新たに追加されたアプリケーション ID。追加がなければ省略、またはアプリケーションメンバーシップに影響しない場合も省略。 |
| removedApplicationIds | string[] | ✅ | 削除されたアプリケーション ID。削除がなければ省略。 |
4 つの delta 配列は オプションかつ加算的 です。これらを期待しないコンシューマーの既存ペイロード構造を壊さず、従来の 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 配列は 完全に省略 されます。例えば、PUT /organizations/:id/users で既存セットと同じメンバーシップに置き換えた場合、実質的な変更がないためペイロードは { organizationId } だけになり、4 つの delta フィールドはすべて省略されます。同様に、既存メンバーの再追加や既にメンバーの招待再承認も同様です。
コンシューマーはフィールドが存在しない場合「その側に変更なし」とみなす必要があります(「空の変更」とはみなさないでください)。
配列ごとの上限(サイレント切り捨て)
各 delta 配列は 最大 5000 件 で上限されます。1 回の Management API 呼び出しで 5000 人(またはアプリケーション)を超えて追加・削除した場合、該当 delta 配列は先頭 5000 件にサイレントに切り捨てられます。ペイロード内に上限発動のマーカーはありません。
管理者による一括操作で 1 回の呼び出しで 5000 件超のメンバーに影響する可能性がある場合、配列がちょうど 5000 件なら Management API で正規メンバーシップを再取得してください:
GET /organizations/:id/users:全ユーザーメンバーシップGET /organizations/:id/applications:全アプリケーションメンバーシップ
これは GitHub の push イベントが commits を 20 件で上限し、完全リストは compare API で取得するパターンと同じです。
no-op イベントのスキップ
コンシューマー側で no-op 配信(delta フィールドがないイベント)をスキップするには、delta 配列の存在でフィルタしてください:
if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// 実際のメンバーシップ変更があった場合のみ処理
}
?.length は undefined と [] の両方で falsy になるため、フィールドが存在しない場合も(将来空配列で出力される場合も)同じ条件で堅牢に判定できます。
ペイロード例
ユーザー追加(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"]
}
既存メンバーの再追加、no-op の 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, 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 | 送信レート制限 に達したメールアドレスまたは電話番号。 |
Grant.LimitExceeded
認可 (Authorization) 成功時に、ユーザーがアプリの 最大同時認証デバイス数(maxAllowedGrants)を超え、Logto がそのアプリの最も古いグラントを失効させた場合に発火します。
このイベントは OIDC 認可 (Authorization) エンドポイントから発行されるため、interactionEvent や sessionId は 含まれません。共通フィールドと ip に加え、ボディには以下が含まれます:
| フィールド | 型 | オプション | 備考 |
|---|---|---|---|
| userId | string | グラントが失効されたユーザー。 | |
| applicationId | string | maxAllowedGrants 上限を超えたアプリケーション。 | |
| application | ApplicationEntity | ✅ | アプリケーションエンティティ。配信時に解決できない場合は省略。 |
| maxAllowedGrants | number | イベント発火時のアプリケーション設定上限。 | |
| preRevocationActiveGrantCount | number | 失効前にこのアプリでユーザーが保持していたアクティブグラント数(今回発行分も含む)。 | |
| revokedGrantIds | string[] | 実際に失効されたグラント ID(古い順)。 |
ペイロード例:
{
"hookId": "hook_abc",
"deliveryId": "3nfp8xk1q2z7",
"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 では解決できません(アクティブ グラントのみ返します)。必要ならペイロードを記録してください。 - 実際に 1 件以上のグラントが 失効された場合のみ イベントが発火します。上限内であればイベントは発火しません。
- 上限超過のたびに発火するため、許可数を超えるデバイスから繰り返しサインインすると、その都度イベントが発生します。
- 配信は fire-and-forget 方式です。遅い・失敗するエンドポイントでもユーザーの認可 (Authorization) をブロックしません。失敗した配信も他の Webhook と同様に監査ログに記録されます。