Webhook
通知先の登録、署名検証、イベント、テスト送信、配送失敗への対処。
Webhookは、コンテンツの変更を外部のURLへHTTP POSTで通知する機能です。サイトの再ビルドやキャッシュ再検証に使います。記事本文を届けるのではなく、変更を知らせる小さなJSONを送ります。
設定にはオーナーまたは保守担当の権限と、外部からアクセスできるHTTPSの受信URLが必要です。
1. 通知先を準備する
ホスティング先のデプロイフック、または自分のアプリの受信処理を用意します。Next.jsの再検証に使う場合は、プラグインのnextRevalidateを利用できます。
自前の受信処理では、通知を受けて何を更新するか決めます。記事が削除された場合や、entryがないテスト通知も扱えるようにしてください。
2. Webhookを登録する
- 対象APIの「設定」(
/w/your-workspace/apis/blog/settings)を開きます。 - 「Webhookを追加」から、名前・通知先URLを入力します。
- 署名検証する場合はシークレットを設定します。
- 「追加する」を押し、有効になっていることを確認します。
1つのAPIにつき5件まで登録できます。イベントごとの選択はなく、登録したAPIの各イベントを通知します。不要なイベントは受信側で判定します。一時停止するときは無効に切り替えます。
シークレットは受信側の設定にも同じ値を登録します。APIキーとは別の値です。省略すると署名ヘッダーは送られません。
通知先はHTTPSのみです。外部からアクセスできる受信URLを用意してください。リダイレクトは追跡しないため、最終的な受信URLを設定してください。プライベートアドレスやループバックなど公開されていないアドレスに解決される宛先は拒否されるため、localhostや社内ネットワークは指定できません。署名用のシークレットは暗号化して保存されます。
3. テスト送信する
一覧の操作メニューから「テスト送信」を実行し、受信先のログと送信結果を確認します。2xxが返れば配送成功です。テストではeventがwebhook.test、entryがnullになります。
テスト送信は、過去に失敗したイベントの再送ではありません。 実際の記事更新の動作確認には、対象記事を保存して受信側まで確認してください。
イベント
| イベント | 発生する操作 |
|---|---|
entry.created | 新規保存。下書きの作成も含む |
entry.updated | 既存記事の保存、手動の公開状態変更、履歴の復元など |
entry.deleted | コンテンツの削除 |
entry.published | 予約公開を定期処理で確定 |
entry.unpublished | 日時による公開終了を定期処理で確定 |
webhook.test | テスト送信 |
手動で「公開」にして保存したときは、作成/更新イベントです。公開に関する処理をentry.publishedだけに限定すると、手動公開を取りこぼします。また、公開から下書きへの変更では、entry.updatedの新しい状態がdraftになるため、古い公開ページの無効化も必要です。
公開中の原稿を「下書きとして保存」しても、未公開の変更を破棄してもWebhookは送られません。未公開の変更を「保存する」で公開すると、通常のentry.updatedが送られます。
ペイロードとヘッダー
{ "event": "entry.updated", "workspace": "your-workspace", "api": "blog", "entry": { "id": "abcDEF123456", "status": "published" }, "triggeredAt": "2026-09-01T01:00:00.000Z"}entry.statusはdraft・published・scheduled・closedです。削除通知には削除前の状態が入ります。本文が必要なら、必要なタイミングで配信APIから取得します。
多言語のAPIでサブ言語版が変更された通知には、トップレベルに言語コードのlangが付きます。メイン言語の通知には付きません。entry.idは言語をまたいで同じです。たとえば英語版の保存では次の形になります。
{ "event": "entry.updated", "workspace": "your-workspace", "api": "blog", "lang": "en", "entry": { "id": "abcDEF123456", "status": "published" }, "triggeredAt": "2026-09-01T01:00:00.000Z"}受信側でlangの有無を確認し、言語別のURLやキャッシュを更新してください。言語版の作成・更新・削除に加え、予約公開と公開終了の確定も対象です。既存のメイン言語のペイロードは変わりません。
| ヘッダー | 値 |
|---|---|
content-type | application/json |
user-agent | genko-webhook/1.0 |
x-genko-event | イベント名 |
x-genko-timestamp | シークレット設定時のみ。配信試行時のUnix秒 |
x-genko-signature | シークレット設定時のみ。timestamp.rawBodyのHMAC-SHA256をsha256=付きの16進文字列で送信 |
署名を検証する
次はWeb標準のRequest/Responseを使う受信処理の例です。JSONへ変換する前の生の本文に対して署名を検証します。SDKは既定で現在時刻との差が300秒を超えるタイムスタンプを拒否し、署名を定数時間で比較します。再送時には新しいタイムスタンプと署名が付与されます。
import { verifyGenkoSignature } from "@genko-me/sdk";
export async function POST(request: Request) { const secret = process.env.GENKO_WEBHOOK_SECRET; if (!secret) return new Response("Webhook secret is not configured", { status: 500 });
const rawBody = await request.text(); const valid = await verifyGenkoSignature({ secret, rawBody, signatureHeader: request.headers.get("x-genko-signature"), timestampHeader: request.headers.get("x-genko-timestamp"), }); if (!valid) return new Response("Invalid signature", { status: 401 });
const payload = JSON.parse(rawBody); // 利用するworkspace・api・eventと、必要な値の形を検証してから処理します。 return Response.json({ received: true, event: payload.event });}この例は受信確認までです。実際には再検証や処理キューへの登録など、必要な処理を追加します。重い処理を同期で待たず、同じ更新を繰り返し処理しても破綻しない設計にしてください。
配送の制限と失敗時
タイムアウトは10秒で、自動リトライはありません。一覧の操作メニューから「配信ログ」を開き、対象の配信を選びます。「送ったもの」「返ってきたもの」でURL・ヘッダー・本文、HTTPステータス、所要時間や接続エラーを確認できます。
記録は30日間保持します。レスポンス本文は先頭4KBまでで、長い場合は切り詰めたことが表示されます。リクエストの署名は一部を伏せ、レスポンスのSet-Cookieは記録しません。
| 結果 | 確認すること |
|---|---|
| 301/302など | リダイレクト先の最終URLを登録しているか |
| 401 | シークレットが一致するか。生の本文で検証しているか |
| 404 | 受信ルートが本番へデプロイされているか |
| タイムアウト | 10秒以内に応答できるか |
| 成功だが表示が古い | 受信後の再ビルド・再検証対象が正しいか |
受信先を修正したら、対象の配信ログで「もう一度送る」を押して手動で再送できます。再送する本文は元の配信と同じで、triggeredAtも元の日時のままです。送信先URLと署名用シークレットは現在のWebhook設定を使い、新しい配信結果を記録します。
再送で同じイベントを複数回受け取ることがあるため、受信側は重複して処理できるようにします。保存期間を過ぎた通知については、最新データを取り直す、再ビルドを手動実行するなどの方法を用意してください。テスト送信の成功だけでは、失敗した処理を復旧したことにはなりません。
最終更新: 2026年10月16日