Webhookイベントリファレンス
すべてのZenFlip Webhookイベントタイプの詳細なペイロードスキーマとJSON例。リクエストヘッダーとNode.js署名検証の例を含みます。
Webhookイベントリファレンス
このドキュメントでは、ZenFlipがサポートする各Webhookイベントタイプの完全なペイロードスキーマとJSON例を提供します。すべてのペイロードは同じトップレベル構造に従います。
共通エンベロープ
すべてのWebhookペイロードはこの構造を共有します:
`json { "id": "wh_del_{uuid}", "event": "{event_type}", "createdAt": "ISO 8601 timestamp", "organizationId": "{org_uuid}", "data": { } } `
リクエストヘッダー
すべてのWebhook HTTP POSTリクエストには以下のヘッダーが含まれます:
ヘッダー | 値の例 |
|---|---|
|
|
|
|
|
|
|
|
|
|
publication.created
組織で新しいパブリケーションが作成されたときに発火します。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | 新しいパブリケーションのUUID |
| string | パブリケーションタイトル |
| string | URLフレンドリーなスラッグ |
| string | 初期ステータス(常に |
| string | 作成したユーザーのUUID |
ペイロード例
`json { "id": "wh_del_a1b2c3d4-0001-0001-0001-000000000001", "event": "publication.created", "createdAt": "2026-02-20T12:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "slug": "q1-sales-report", "status": "draft", "createdBy": "550e8400-e29b-41d4-a716-446655440000" } } `
publication.converted
PDF変換ジョブが完了したときに発火します(成功・エラーを問わず)。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | パブリケーションのUUID |
| string | パブリケーションタイトル |
| string | 変換されたバージョンのUUID |
| integer | 連番のバージョン番号 |
| string | 変換結果: |
| integer | 生成されたページ数(失敗時は0) |
| string | エラーメッセージ(成功時はnull) |
| string | 変換完了のISO 8601タイムスタンプ |
ペイロード例(成功)
`json { "id": "wh_del_a1b2c3d4-0002-0002-0002-000000000002", "event": "publication.converted", "createdAt": "2026-02-20T14:05:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "versionId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "versionNumber": 1, "status": "ready", "pageCount": 24, "conversionError": null, "convertedAt": "2026-02-20T14:05:00.000Z" } } `
ペイロード例(失敗)
`json { "id": "wh_del_a1b2c3d4-0002-0002-0002-000000000003", "event": "publication.converted", "createdAt": "2026-02-20T14:06:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "versionId": "d4e5f6a7-b8c9-0123-def0-234567890123", "versionNumber": 2, "status": "failed", "pageCount": 0, "conversionError": "PDF file is password-protected and cannot be processed", "convertedAt": "2026-02-20T14:06:00.000Z" } } `
publication.deleted
パブリケーションが完全に削除されたときに発火します。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | 削除されたパブリケーションのUUID |
| string | 削除時のタイトル |
| string | 削除時のスラッグ |
| string | 削除したユーザーのUUID |
ペイロード例
`json { "id": "wh_del_a1b2c3d4-0003-0003-0003-000000000004", "event": "publication.deleted", "createdAt": "2026-02-20T16:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "publicationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Q1 Sales Report", "slug": "q1-sales-report", "deletedBy": "550e8400-e29b-41d4-a716-446655440000" } } `
lead.captured
閲覧者がパブリケーション内のリードキャプチャフォームを送信したときに発火します。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | キャプチャされたリードのUUID |
| string | パブリケーションのUUID |
| string | 送信されたメールアドレス |
| string | 氏名(未収集の場合はnull) |
| string | 会社名(未収集の場合はnull) |
| string | 電話番号(未収集の場合はnull) |
| object | カスタムフォームフィールドのキーバリューペア |
| integer | フォームがトリガーされたページ |
| string | トリガー方法: |
| string | 送信のISO 8601タイムスタンプ |
ペイロード例
`json { "id": "wh_del_a1b2c3d4-0004-0004-0004-000000000005", "event": "lead.captured", "createdAt": "2026-02-20T15:30:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "leadId": "d4e5f6a7-b8c9-0123-def0-234567890123", "publicationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "email": "jane@example.com", "name": "Jane Doe", "company": "Acme Inc.", "phone": "+1-555-0100", "customFields": { "jobTitle": "Product Manager", "department": "Marketing" }, "sourcePage": 3, "triggerType": "page", "capturedAt": "2026-02-20T15:30:00.000Z" } } `
team.member_joined
チームメンバーが招待を承認し、組織に参加したときに発火します。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | 新しいチームメンバーのUUID |
| string | メンバーのメールアドレス |
| string | 表示名 |
| string | 割り当てられたロール: |
| string | 招待を送信したユーザーのUUID |
| string | 参加時のISO 8601タイムスタンプ |
ペイロード例
`json { "id": "wh_del_a1b2c3d4-0005-0005-0005-000000000006", "event": "team.member_joined", "createdAt": "2026-02-20T17:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "userId": "880e8400-e29b-41d4-a716-446655440003", "email": "neweditor@example.com", "name": "Jordan Lee", "role": "editor", "invitedBy": "550e8400-e29b-41d4-a716-446655440000", "joinedAt": "2026-02-20T17:00:00.000Z" } } `
export.completed
HTMLまたはSCORMエクスポートジョブの処理が完了したときに発火します。
ペイロードスキーマ
フィールド | 型 | 説明 |
|---|---|---|
| string | エクスポートジョブのUUID |
| string | エクスポートされたパブリケーションのUUID |
| string | エクスポート形式: |
| string | 結果: |
| string | エクスポートをダウンロードするための署名付きURL(失敗時はnull、24時間で期限切れ) |
| integer | エクスポートファイルのバイト数(失敗時は0) |
| string | エラーメッセージ(成功時はnull) |
| string | 完了のISO 8601タイムスタンプ |
ペイロード例
`json { "id": "wh_del_a1b2c3d4-0006-0006-0006-000000000007", "event": "export.completed", "createdAt": "2026-02-20T18:00:00.000Z", "organizationId": "660e8400-e29b-41d4-a716-446655440000", "data": { "exportJobId": "f6a7b8c9-d0e1-2345-f012-456789012345", "publicationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "scorm_12", "status": "completed", "downloadUrl": "https://cdn.zenflip.io/exports/f6a7b8c9...?token=abc123&expires=1740182400", "fileSizeBytes": 15728640, "error": null, "completedAt": "2026-02-20T18:00:00.000Z" } } `
署名検証(完全な例)
以下はZenFlip Webhookを受信・検証する完全なNode.js Expressサーバーの例です:
`javascript const express = require("express"); const crypto = require("crypto");
const app = express(); const WEBHOOK_SECRET = process.env.ZENFLIP_WEBHOOK_SECRET;
// 重要:署名検証のために生のボディを取得するにはexpress.raw()を使用 app.post( "/webhooks/zenflip", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-zenflip-signature"]; const eventType = req.headers["x-zenflip-event"]; const deliveryId = req.headers["x-zenflip-delivery-id"];
// ステップ1:署名の検証 const expected = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(req.body, "utf8") .digest("hex");
const isValid = crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex") );
if (!isValid) { console.error("Webhook signature verification failed:", deliveryId); return res.status(401).json({ error: "Invalid signature" }); }
// ステップ2:ペイロードのパース const event = JSON.parse(req.body.toString());
// ステップ3:即座に確認応答 res.status(200).json({ received: true });
// ステップ4:イベントを非同期で処理 handleEvent(event).catch((err) => { console.error("Error processing webhook:", err); }); } );
async function handleEvent(event) { switch (event.event) { case "publication.created": console.log("New publication:", event.data.title); break;
case "publication.converted": if (event.data.status === "ready") { console.log( "Conversion complete:", event.data.title, "-", event.data.pageCount, "pages" ); } else { console.error("Conversion failed:", event.data.conversionError); } break;
case "lead.captured": console.log("New lead:", event.data.email, "from page", event.data.sourcePage); // CRMへの転送、通知の送信など break;
case "team.member_joined": console.log("New team member:", event.data.name, "as", event.data.role); break;
case "export.completed": if (event.data.status === "completed") { console.log("Export ready:", event.data.downloadUrl); } break;
default: console.log("Unhandled event type:", event.event); } }
app.listen(3000, () => { console.log("Webhook server listening on port 3000"); }); `
ベストプラクティス
必ず署名を検証してください。
X-ZenFlip-Signatureヘッダーを検証せずにWebhookペイロードを信頼しないでください。素早く応答してください。
200ステータスを即座に返し、イベントは非同期で処理してください。Webhook配信は10秒でタイムアウトします。配信IDで重複を排除してください。 処理済みの
id値を保存し、重複をスキップしてください。リトライにより同じイベントが複数回配信される場合があります。HTTPSエンドポイントを使用してください。 ZenFlipはHTTPSを使用するURLにのみWebhookを送信します。
障害を適切に処理してください。 下流のサービスが利用できない場合は、エラーステータスを返すのではなく、後で処理するためにイベントをキューに入れてください。