# 機能設計書 141-Webhooks

## 概要

本ドキュメントは、Chatwootにおける外部システムへのWebhook通知機能の設計書である。Webhookを使用して、アカウント内で発生したイベント（会話作成、メッセージ送受信、連絡先更新など）を外部システムへリアルタイムに通知する仕組みを提供する。

### 本機能の処理概要

**業務上の目的・背景**：企業のカスタマーサポート業務では、Chatwootで発生したイベントを外部の業務システム（CRM、通知サービス、分析ツールなど）と連携させる必要がある。Webhookは、サードパーティシステムとの疎結合な統合を可能にし、リアルタイムでのデータ同期を実現する。これにより、カスタマーサポートのワークフローを自動化し、業務効率を向上させることができる。

**機能の利用シーン**：
- 新しい会話が作成された際にSlackやTeamsに通知を送信する
- 顧客からのメッセージを受信した際にCRMシステムに記録する
- 会話ステータスが変更された際に外部の分析システムにデータを送信する
- 連絡先情報が更新された際にマーケティングオートメーションツールと同期する

**主要な処理内容**：
1. WebhookエンドポイントのCRUD操作（作成・取得・更新・削除）
2. Webhookサブスクリプションの管理（購読するイベントタイプの設定）
3. WebhookタイプによるスコープのCategory分け（アカウント単位/インボックス単位）
4. URL形式のバリデーションとサブスクリプションイベントの検証

**関連システム・外部連携**：外部システムのWebhook受信エンドポイントへHTTP POSTリクエストでJSONペイロードを送信する。RestClientを使用した非同期のHTTP通信を行う。

**権限による制御**：Webhookの作成・編集・削除は管理者権限（Administrator）を持つユーザーのみが実行可能である。一般のエージェントは閲覧のみ許可される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 67 | Webhook設定画面 | 主画面 | Webhookの設定、URL入力、イベント購読の設定 |

## 機能種別

CRUD操作 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | String | Yes | Webhook送信先URL | HTTP/HTTPS形式のURL、アカウント内でユニーク |
| subscriptions | Array | Yes | 購読するイベントの配列 | ALLOWED_WEBHOOK_EVENTSに含まれるイベントのみ許可 |
| name | String | No | Webhookの名前 | - |
| inbox_id | Integer | No | インボックスID（inbox_type用） | 存在するインボックスID |

### 許可されるWebhookイベント

```
- conversation_status_changed
- conversation_updated
- conversation_created
- contact_created
- contact_updated
- message_created
- message_updated
- webwidget_triggered
- inbox_created
- inbox_updated
- conversation_typing_on
- conversation_typing_off
```

### 入力データソース

管理画面のWebhook設定フォームからの入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | WebhookのID |
| url | String | Webhook送信先URL |
| subscriptions | Array | 購読イベントリスト |
| name | String | Webhook名 |
| webhook_type | String | account_type / inbox_type |
| account_id | Integer | 所属アカウントID |
| inbox_id | Integer | インボックスID（inbox_type時） |
| created_at | DateTime | 作成日時 |
| updated_at | DateTime | 更新日時 |

### 出力先

- API レスポンス（JSON形式）
- webhooksテーブル（データベース）

## 処理フロー

### 処理シーケンス

```
1. Webhook作成リクエスト受信
   └─ API::V1::Accounts::WebhooksController#create

2. 認可チェック
   └─ check_authorization により管理者権限を確認

3. パラメータ検証
   └─ URLの形式検証
   └─ サブスクリプションイベントの検証
   └─ URL重複チェック（アカウント内）

4. Webhookレコード作成
   └─ Current.account.webhooks.new(webhook_params)
   └─ webhook_typeの自動判定（inbox_idの有無）

5. レスポンス返却
   └─ 作成したWebhook情報をJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{認可チェック}
    B -->|失敗| C[403 Forbidden]
    B -->|成功| D[パラメータ検証]
    D -->|URL無効| E[422 Unprocessable Entity]
    D -->|サブスクリプション無効| E
    D -->|URL重複| E
    D -->|成功| F[Webhookレコード作成]
    F --> G[webhook_type設定]
    G -->|inbox_id有り| H[inbox_type]
    G -->|inbox_id無し| I[account_type]
    H --> J[DB保存]
    I --> J
    J --> K[201 Created]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-141-01 | URL一意性 | 同一アカウント内でWebhook URLは一意である必要がある | Webhook作成・更新時 |
| BR-141-02 | URLプロトコル | HTTPまたはHTTPSスキームのみ許可 | Webhook作成・更新時 |
| BR-141-03 | サブスクリプション必須 | 最低1つ以上のイベントを購読する必要がある | Webhook作成・更新時 |
| BR-141-04 | イベント制限 | ALLOWED_WEBHOOK_EVENTSに定義されたイベントのみ購読可能 | Webhook作成・更新時 |
| BR-141-05 | Webhookタイプ判定 | inbox_idが指定された場合はinbox_type、それ以外はaccount_type | Webhook作成時 |

### 計算ロジック

特になし

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| index | webhooks | SELECT | アカウントに紐づくWebhook一覧取得 |
| create | webhooks | INSERT | 新規Webhook作成 |
| update | webhooks | UPDATE | 既存Webhook更新 |
| destroy | webhooks | DELETE | Webhook削除 |

### テーブル別操作詳細

#### webhooks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | account_id = Current.account.id | 一覧取得 |
| INSERT | url | リクエストパラメータのurl | URLは一意 |
| INSERT | subscriptions | リクエストパラメータのsubscriptions | JSON配列 |
| INSERT | name | リクエストパラメータのname | 任意 |
| INSERT | inbox_id | リクエストパラメータのinbox_id | inbox_type時のみ |
| INSERT | webhook_type | inbox_id有無で自動判定 | enum: account_type(0), inbox_type(1) |
| INSERT | account_id | Current.account.id | 必須 |
| UPDATE | url, subscriptions, name | リクエストパラメータ | 更新対象項目 |
| DELETE | - | id = params[:id] | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 管理者権限がない場合 | 管理者でログインする |
| 404 | Not Found | 指定されたWebhookが存在しない | 正しいIDを指定する |
| 422 | Unprocessable Entity | URL形式が不正 | 正しいURL形式で入力する |
| 422 | Unprocessable Entity | URLが重複 | 別のURLを使用する |
| 422 | Unprocessable Entity | サブスクリプションが不正 | 許可されたイベントを指定する |

### リトライ仕様

Webhookエンドポイント自体にはリトライ仕様は存在しない（WebhookJobで別途定義）

## トランザクション仕様

- 各CRUD操作は単一トランザクションで処理
- 監査ログ（Enterprise版）は同一トランザクション内で記録

## パフォーマンス要件

- 一覧取得: 100件以下のWebhookで100ms以内
- 作成・更新・削除: 500ms以内

## セキュリティ考慮事項

- 管理者権限のみがWebhookを管理可能
- URL検証によりHTTP/HTTPSのみ許可（任意のプロトコルを防止）
- アカウントスコープによるデータ分離
- Enterprise版では監査ログで操作履歴を記録

## 備考

- WebhookのURLはアカウント内でユニークである必要がある（インデックス: index_webhooks_on_account_id_and_url）
- Enterprise版ではAudit::Webhookモジュールによる監査ログ機能が追加される

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Webhookのデータモデルと許可されるイベントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | webhook.rb | `app/models/webhook.rb` | Webhookモデルの定義、バリデーション、enum定義 |

**読解のコツ**:
- **20-27行目**: アソシエーションとバリデーションの定義。URLの形式検証とユニーク制約に注目
- **27行目**: `webhook_type`のenum定義（account_type: 0, inbox_type: 1）
- **29-31行目**: `ALLOWED_WEBHOOK_EVENTS`定数で許可されるイベントタイプを確認
- **35-40行目**: `validate_webhook_subscriptions`メソッドでサブスクリプションの検証ロジックを確認

#### Step 2: エントリーポイントを理解する

API コントローラーの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | webhooks_controller.rb | `app/controllers/api/v1/accounts/webhooks_controller.rb` | CRUD操作のエントリーポイント |

**主要処理フロー**:
1. **2行目**: `before_action :check_authorization` で認可チェック
2. **5-7行目**: `index`アクションでアカウントのWebhook一覧取得
3. **9-12行目**: `create`アクションで新規Webhook作成
4. **14-16行目**: `update`アクションでWebhook更新
5. **18-21行目**: `destroy`アクションでWebhook削除
6. **25-27行目**: `webhook_params`でStrong Parametersによるパラメータ許可

#### Step 3: ビュー（JBuilder）を理解する

APIレスポンスの構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.json.jbuilder | `app/views/api/v1/accounts/webhooks/index.json.jbuilder` | 一覧APIのレスポンス形式 |
| 3-2 | create.json.jbuilder | `app/views/api/v1/accounts/webhooks/create.json.jbuilder` | 作成APIのレスポンス形式 |

### プログラム呼び出し階層図

```
API::V1::Accounts::WebhooksController
    │
    ├─ #index
    │      └─ Current.account.webhooks
    │
    ├─ #create
    │      └─ Current.account.webhooks.new(webhook_params)
    │             └─ Webhook#validate_webhook_subscriptions
    │
    ├─ #update
    │      ├─ fetch_webhook
    │      └─ @webhook.update!(webhook_params)
    │             └─ Webhook#validate_webhook_subscriptions
    │
    └─ #destroy
           ├─ fetch_webhook
           └─ @webhook.destroy!
```

### データフロー図

```
[入力]                      [処理]                           [出力]

Webhook設定画面    ───▶ WebhooksController          ───▶ webhooksテーブル
(URL, subscriptions,       │                                │
 name, inbox_id)           ▼                                ▼
                     Webhook Model              JSON Response
                     (バリデーション)            (JBuilder)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| webhook.rb | `app/models/webhook.rb` | モデル | Webhookデータモデル定義 |
| webhooks_controller.rb | `app/controllers/api/v1/accounts/webhooks_controller.rb` | コントローラー | WebhookのCRUD API |
| index.json.jbuilder | `app/views/api/v1/accounts/webhooks/index.json.jbuilder` | ビュー | 一覧APIレスポンス |
| create.json.jbuilder | `app/views/api/v1/accounts/webhooks/create.json.jbuilder` | ビュー | 作成APIレスポンス |
| update.json.jbuilder | `app/views/api/v1/accounts/webhooks/update.json.jbuilder` | ビュー | 更新APIレスポンス |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| apps.yml | `config/integration/apps.yml` | 設定 | Webhook統合アプリ設定 |
