<!-- https://unlockos.io/ja/manual/webhook-channel -->

# Webhook 通知チャネル ヘルプ

## 概要

**Webhook** は、[通知ワークフロー](notification-workflow.md)の **6番目のチャネル**です。LINE / Email / SMS / Voice / スピーカーと同じ「アンカーイベント + オフセット」の仕組みで、予約に起きた出来事を**任意の外部エンドポイントへ HTTPS POST** で届けます。自社システムや外部サービス（在庫連携・分析基盤・カスタム通知先など）に予約イベントを流し込みたいときに使います。

サイドバーの「🔔 通知ワークフロー」→ **チャネル** タブの Webhook カードから `/notifications/channels/webhook`（配送先管理）を開きます。

> **この機能は施設が所属する組織単位で有効化されます。** チャネルタブに Webhook カードが表示されない場合は、`notification_webhook_channel` フィーチャーフラグが組織で OFF です。サポートに有効化をご依頼ください。

Webhook を実際に発火させるには、配送先を登録したあとで**通知ワークフローの編集画面**（ワークフロータブ）でチャネルに「Webhook」を選び、配送先を指定します。配送先の登録・鍵管理はこのページ、発火条件（どのイベントで送るか）の設定は[通知ワークフロー](notification-workflow.md)側という役割分担です。

---

## 詳細機能

## 機能1: 配送先（エンドポイント）の登録

1. URL 欄に受信側のエンドポイント URL を入力する（**https のみ**）
2. 説明欄（任意）に配送先の識別用メモを入力する
3. 「追加」をクリック

保存が成功すると、その場に**署名鍵が1回だけ表示**されます。この画面を離れると再表示されないため、その場で受信側のシステムに控えてください（後から必要になったら「鍵を表示」で再取得できます。[機能2](#機能2-署名鍵とローテーション)参照）。

登録した配送先は一覧にカード形式で並び、以下を行えます。

| 操作 | 内容 |
|------|------|
| 編集 | URL・説明を変更する |
| 有効／無効の切り替え | 無効にすると、この配送先宛の送信は行われなくなる（登録情報は残る） |
| 鍵を表示 | 現在の署名鍵を再表示する |
| 鍵をローテート | 署名鍵を新しく発行し直す（[機能2](#機能2-署名鍵とローテーション)参照） |

> **配送先の削除はできません。** 使わなくなった配送先は「無効」に切り替えてください（過去の送信履歴・紐づくワークフロー設定を壊さない設計のためです）。

### URL の制約（登録できないURL）

登録・保存の時点と、実際に送信する直前の**両方**で URL が検証されます。次の URL は登録できません。

- `https:` 以外（`http:` は不可）
- ホスト名が private / loopback / link-local などの内部アドレスに解決される URL
- 数値表記の IP など、上記を迂回する記法

一度登録できた URL でも、DNS の向き先を後から内部アドレスに変更すると、次回送信時に拒否されます。

### 組織共通の配送先

同じ組織の別施設が登録した配送先がある場合、「組織共通」バッジが付いた行として一覧に表示されます。これは閲覧のみで、**施設側の画面からは編集・無効化・鍵の操作ができません**（URLも伏せられ、識別できる範囲のホスト名のみ表示されます）。組織共通の配送先へもイベントは届くため、[通知ワークフロー](notification-workflow.md)の配送先選択には候補として現れます。

---

## 機能2: 署名鍵とローテーション

すべてのリクエストには、受信側で検証できるように HMAC 署名が付与されます。

### 送られてくる署名の中身

| ヘッダ | 内容 |
|---|---|
| `X-UnlockOS-Signature` | `sha256=` に続けて署名（base64）。ローテーション中は空白区切りで2つ並ぶことがある |
| `X-UnlockOS-Timestamp` | 署名対象に含まれる送信時刻（Unix秒） |
| `X-UnlockOS-Endpoint-Id` | どの配送先（＝どの鍵）で検証すべきかの識別子 |
| `X-UnlockOS-Delivery-Id` | 配送単位の冪等キー（本文の `eventId` と同じ値） |
| `X-UnlockOS-Delivery-Attempt` | 何回目の送信か（1始まり、リトライで増える） |
| `X-UnlockOS-Event-Type` | 本文の `eventType` と同じ値 |

署名は `base64(HMAC-SHA256(署名鍵, "送信時刻" + "." + "本文の生バイト列"))` で計算されています。受信側での検証手順は次のとおりです。

1. `X-UnlockOS-Endpoint-Id` から、自分の記録にある対応する署名鍵を選ぶ（本文に入っている組織・施設IDを鍵の選択に使わないこと。検証前の値は信用できません）
2. その鍵と、受け取った `X-UnlockOS-Timestamp`・**受信した生の本文バイト列**（JSONとしてパースし直したものではない）を使って同じ計算を行う
3. 計算結果と `X-UnlockOS-Signature` を比較する（`sha256=` は複数並ぶことがあるので、**どれか1つが一致すれば受理**する。1つしか一致しない前提で実装すると、後述のローテーション中に配送が全部失敗扱いになる）
4. `X-UnlockOS-Timestamp` が現在時刻から大きくずれていないか確認する（目安は前後5分以内。リプレイ攻撃対策）
5. 検証・時刻確認に成功しても、同じ `eventId` を過去に処理済みなら破棄する（配送は最低1回保証のため、同じイベントが複数回届くことがあります）

### 鍵のローテーション

「鍵をローテート」を実行すると、新しい鍵が発行され、直前まで使っていた鍵は**1世代だけ有効なまま**残ります。受信側の鍵を差し替えるまでの間も検証が通り続けるため、無停止でローテーションできます。実行前に確認メッセージが表示されます。

「鍵を表示」で現在の鍵を再表示できますが、短時間に繰り返すと一時的に制限されます（不正な連続アクセスを防ぐための仕組みです）。時間をおいて再度お試しください。

---

## 機能3: 送信されるペイロードの形

送信される本文（エンベロープ）は次の形です。**組織・施設・環境などの情報はシステムが自動的に組み立てており、施設オーナーが編集することはできません**（テナント情報を編集可能にすると、詐称や記載漏れで受信側が別テナントのイベントと取り違える恐れがあるためです）。

```jsonc
{
  "schemaVersion": "1",
  "eventType": "reservation.created",
  "eventId": "配送単位の冪等キー（リトライで不変）",
  "sentAt": "送信時刻（ISO 8601）",
  "environment": "production | staging | development",

  "endpoint": { "id": "配送先のID" },
  "organization": { "id": "...", "name": "..." },
  "facility": { "id": "...", "name": "...", "slug": "...", "timezone": "Asia/Tokyo" },
  "workflow": { "id": "...", "key": "...", "anchorEvent": "..." },
  "source": { "type": "reservation" },

  "data": { },        // eventType ごとに形が変わる部分
  "customData": { }   // 施設オーナーが任意に足せる領域（現状のUIからは未設定。将来のワークフロー設定で対応予定）
}
```

- **ゲストの氏名・メールアドレス・電話番号は含まれません。** 個人情報は最小化する方針です
- `environment` があるため、ステージングと本番が同じ受信URLに届いても区別できます
- `facility.slug` は変更されうるため、受信側でのルーティングには使わず `facility.id` / `organization.id` を使ってください

### v1 で送られるイベント一覧

| `eventType` | 送られるタイミング |
|---|---|
| `reservation.created` | 予約が作成された |
| `reservation.cancelled` | 予約がキャンセルされた |
| `reservation.extended` | 予約が延長された |
| `reservation.approval_pending` | 承認待ちになった |
| `reservation.started` | 予約開始時刻に達した |
| `reservation.ended` | 予約終了時刻に達した |
| `checkin.completed` | チェックインが完了した |
| `checkout.completed` | チェックアウトが完了した |
| `payment.completed` | 決済が完了した |

どのイベントで Webhook を発火させるかは、[通知ワークフロー](notification-workflow.md)のワークフロー編集画面でアンカーイベントを選んで設定します。

---

## 機能4: リトライと失敗時の挙動

| 応答 | 挙動 |
|---|---|
| 2xx | 成功。履歴タブに「送信」と記録される |
| 429 / 408 | 一時的な失敗としてリトライされる |
| その他の 4xx | **恒久エラー。リトライされません**（宛先設定の誤りとみなされ、再送しても結果が変わらないため） |
| 3xx（リダイレクト） | **恒久エラー。リダイレクトは追従されません**（迂回によるセキュリティリスクを避けるため） |
| 5xx・タイムアウト・接続エラー | 一時的な失敗としてリトライされる（最大5回。それでも失敗すると「失敗」で確定） |

- 応答待ちのタイムアウトは5秒です
- リトライされても `eventId` と署名対象の本文は変わりません。試行回数（`X-UnlockOS-Delivery-Attempt`）だけが増えます
- 送信結果は[通知ワークフロー](notification-workflow.md)の**履歴タブ**でチャネル「Webhook」として確認できます（他チャネルと同じ画面です）

---

## 機能5: 課金

Webhook チャネルは**無料**です。自前の HTTP 送信のみで外部サービスの実費が発生しないため、SMS・Voice・スピーカーのような従量課金の対象にはなりません。

---

## 機能6: 他チャネルとの使い分け

| チャネル | 宛先 | 「自動選択」の対象 | 課金 |
|---|---|---|---|
| Email / LINE / SMS | ゲストや管理者個人 | ○（到達可能なものへ自動振り分け） | Email/LINE無料、SMSは従量 |
| Voice / スピーカー | 電話・空間内スピーカー | ×（明示指定のみ） | 従量課金 |
| **Webhook** | **外部システムのエンドポイント（人ではない）** | **×（明示指定のみ）** | **無料** |

Webhook は「人に通知する」チャネルではなく、「システムにイベントを連携する」チャネルです。そのため通知ワークフローの「自動選択」では選ばれず、必ずワークフロー編集画面で明示的に「Webhook」を選び、配送先を指定する必要があります。

---

## よくある質問

### Q: 組織共通の配送先の URL を確認できますか？
A: できません。施設側の画面では識別できるホスト名のみが表示され、URL 自体（署名なしでも POST できてしまう情報）は伏せられます。

### Q: 配送先を削除したいのですが
A: 削除はできません。「無効」に切り替えてください。過去の送信履歴やワークフロー設定に影響を与えないための仕様です。

### Q: どのイベントで Webhook が発火しますか？
A: [通知ワークフロー](notification-workflow.md)のワークフロー編集画面で、チャネルに「Webhook」を選んだワークフローのアンカーイベントに従います。標準ワークフローのアンカー・カスタムワークフローのアンカーのどちらでも選べます。

### Q: `customData` はどこで設定しますか？
A: 現在の管理画面には入力欄がありません（今後のアップデートで対応予定）。現状で本文に足せるのはシステムが組み立てる標準フィールドのみです。

### Q: 署名鍵を紛失しました
A: 「鍵を表示」で再表示できます。オーナー権限が必要で、短時間の連続実行は制限されます。

### Q: リダイレクトする URL を登録できますか？
A: 登録はできますが、送信時にリダイレクトは追従されません。3xx 応答は恒久エラーとして扱われます。リダイレクトしない最終的な URL を登録してください。

---

## 関連ページ

- [通知ワークフロー](notification-workflow.md) - アンカーイベント・チャネル選択の設定はこちら
- [スピーカー通知チャネル](space-speaker.md)
