# 機能設計書 169-公開API

## 概要

本ドキュメントは、Chatwootの公開API（Public API）機能の設計を記載する。この機能は外部システムやカスタムアプリケーションがChatwoot APIチャネルを通じて連絡先、会話、メッセージを管理するためのAPIを提供する。

### 本機能の処理概要

公開API機能は、Channel::Api（APIチャネル）を使用して外部システムとChatwootを連携するための機能を提供する。この機能により、独自のチャットインターフェースやモバイルアプリケーションからChatwootのメッセージング機能を利用することができる。

**業務上の目的・背景**：Webウィジェット以外のカスタムチャネルからChatwootを利用するため、汎用的な公開APIが必要である。これにより、モバイルアプリ、IoTデバイス、既存システムなど、様々なプラットフォームからChatwootのカスタマーサポート機能を活用できる。

**機能の利用シーン**：
- モバイルアプリからChatwootのサポートチャットを利用する場面
- 既存のCRMシステムとChatwootを連携する場面
- カスタムチャットUIからメッセージを送受信する場面
- IoTデバイスからサポートリクエストを送信する場面

**主要な処理内容**：
1. 連絡先の作成・取得・更新（Contacts）
2. 会話の作成・取得・一覧表示・ステータス変更（Conversations）
3. メッセージの作成・取得・更新（Messages）

**関連システム・外部連携**：
- Channel::Api（APIチャネル）
- HMAC認証（オプション）
- WebSocket（リアルタイム通知）

**権限による制御**：
- inbox_idとcontact_idによるアクセス制御
- HMAC検証（hmac_mandatory時は必須）
- hmac_verifiedによる会話アクセス範囲制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 119 | 記事ビューア画面 | 参照画面 | ヘルプ記事の表示 |

## 機能種別

CRUD操作 / 外部API

## 入力仕様

### 入力パラメータ（連絡先）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| inbox_id | String | Yes | APIチャネルのidentifier | 有効なidentifier |
| source_id | String | No | 外部システムでの識別子（省略時はUUID自動生成） | - |
| identifier | String | No | ユーザー識別子 | - |
| identifier_hash | String | Conditional | 識別子のHMACハッシュ | hmac_mandatory時は必須 |
| email | String | No | メールアドレス | メール形式 |
| name | String | No | 名前 | - |
| phone_number | String | No | 電話番号 | - |
| avatar_url | String | No | アバター画像URL | 有効なURL形式 |
| custom_attributes | Hash | No | カスタム属性 | - |

### 入力パラメータ（会話）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| contact_id | String | Yes | 連絡先のsource_id | 有効なsource_id |
| custom_attributes | Hash | No | カスタム属性 | - |
| typing_status | String | No | 入力状態（on/off） | 'on' または 'off' |

### 入力パラメータ（メッセージ）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| conversation_id | Integer | Yes | 会話のdisplay_id | 有効なdisplay_id |
| content | String | No | メッセージ内容 | - |
| echo_id | String | No | クライアント側のエコーID | - |
| before | Integer | No | このID以前のメッセージを取得 | 正の整数 |
| attachments | Array[File] | No | 添付ファイル配列 | - |

### 入力データソース

- 外部システムからのHTTPリクエスト

## 出力仕様

### 出力データ（連絡先）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| source_id | String | 外部識別子 |
| pubsub_token | String | WebSocket購読トークン |

### 出力データ（会話）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | 会話の表示ID（display_id） |
| inbox_id | Integer | インボックスID |
| messages | Array | メッセージ一覧 |
| contact | Object | 連絡先情報 |
| custom_attributes | Hash | カスタム属性 |
| status | String | 会話ステータス |

### 出力データ（メッセージ）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | メッセージID |
| content | String | メッセージ内容 |
| message_type | Integer | メッセージタイプ |
| created_at | Integer | 作成日時（UNIX時間） |
| attachments | Array | 添付ファイル一覧 |

### 出力先

- JSON形式のHTTPレスポンス

## 処理フロー

### 処理シーケンス

```
1. 連絡先作成（POST /inboxes/:inbox_id/contacts）
   └─ HMAC検証（必要時）
   └─ ContactInboxWithContactBuilder実行
   └─ source_id, pubsub_tokenを返却

2. 連絡先取得（GET /inboxes/:inbox_id/contacts/:id）
   └─ source_idでcontact_inbox検索
   └─ HMAC検証（必要時）
   └─ 連絡先情報を返却

3. 連絡先更新（PATCH /inboxes/:inbox_id/contacts/:id）
   └─ HMAC検証（必要時）
   └─ ContactIdentifyAction実行
   └─ 更新後の連絡先情報を返却

4. 会話一覧（GET /.../contacts/:contact_id/conversations）
   └─ HMAC検証済みなら全会話、それ以外はcontact_inboxの会話のみ
   └─ 会話一覧を返却

5. 会話作成（POST /.../contacts/:contact_id/conversations）
   └─ ConversationBuilder実行
   └─ 会話情報を返却

6. メッセージ作成（POST /.../conversations/:id/messages）
   └─ メッセージとaddtachment作成
   └─ メッセージ情報を返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[APIチャネル検証]
    B --> C{HMAC必要?}
    C -->|Yes| D{HMAC有効?}
    D -->|No| E[401 Unauthorized]
    D -->|Yes| F[hmac_verified更新]
    C -->|No| F
    F --> G{リソース種別}
    G -->|Contact| H[連絡先処理]
    G -->|Conversation| I[会話処理]
    G -->|Message| J[メッセージ処理]
    H --> K[レスポンス返却]
    I --> K
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-169-01 | source_id自動生成 | 省略時はSecureRandom.uuidで自動生成 | 連絡先作成時 |
| BR-169-02 | HMAC検証 | identifier_hash = HMAC-SHA256(hmac_token, identifier) | hmac_mandatory時 |
| BR-169-03 | 会話アクセス制御 | hmac_verified=trueなら全会話、falseならcontact_inboxのみ | 会話取得時 |
| BR-169-04 | シングル会話ロック | lock_to_single_conversation時は既存会話を返却 | 会話作成時 |
| BR-169-05 | メッセージフィルタ | プライベートメッセージとアクティビティを除外 | メッセージ取得時 |
| BR-169-06 | CSAT更新期限 | 作成から14日以内のみ更新可能 | メッセージ更新時 |

### 計算ロジック

**HMAC検証**:
```ruby
expected_hash = OpenSSL::HMAC.hexdigest('sha256', hmac_token, identifier.to_s)
valid = (identifier_hash == expected_hash)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 連絡先作成 | contacts | INSERT | 連絡先作成 |
| 連絡先作成 | contact_inboxes | INSERT | contact_inbox作成 |
| 連絡先更新 | contacts | UPDATE | 連絡先更新 |
| 連絡先更新 | contact_inboxes | UPDATE | hmac_verified更新 |
| 会話作成 | conversations | INSERT | 会話作成 |
| 会話取得 | conversations | SELECT | 会話一覧取得 |
| メッセージ作成 | messages | INSERT | メッセージ作成 |
| メッセージ作成 | attachments | INSERT | 添付ファイル作成 |

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

#### contact_inboxes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | source_id = params[:id] | 識別子条件 |
| SELECT | - | inbox_id = @inbox_channel.inbox.id | インボックス条件 |
| INSERT | source_id | params[:source_id] または UUID | 外部識別子 |
| UPDATE | hmac_verified | true | HMAC検証済み時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | HMACが無効 | 正しいidentifier_hashを使用 |
| 404 | Not Found | リソースが見つからない | 正しいIDを指定 |
| 422 | Unprocessable Entity | CSAT更新期限超過 | 14日以内に更新 |
| 500 | Internal Server Error | サーバーエラー | ログを確認 |

### リトライ仕様

特になし（同期処理のため）

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

- 連絡先作成: ContactInboxWithContactBuilder内で暗黙的トランザクション
- メッセージ作成: メッセージと添付ファイル作成は暗黙的トランザクション

## パフォーマンス要件

- レスポンス時間: 500ms以内

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

- APIチャネルidentifierによる認証
- HMAC-SHA256による識別子の改ざん防止
- hmac_verifiedによる会話アクセス範囲制御
- プライベートメッセージの自動フィルタリング

## 備考

- WebウィジェットAPIとは異なり、Channel::Apiを使用
- source_idは外部システムでの識別子として使用
- pubsub_tokenはWebSocket接続に使用
- 会話のステータス変更は訪問者をresolverとして記録

---

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

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

### 推奨読解順序

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

APIチャネルに関連するデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | api.rb | `app/models/channel/api.rb` | APIチャネルモデル、identifier、hmac設定 |
| 1-2 | contact_inbox.rb | `app/models/contact_inbox.rb` | source_id、hmac_verified |

**読解のコツ**: Channel::ApiのidentifierがWebウィジェットのwebsite_tokenに相当することを理解すること。

#### Step 2: 基底コントローラーを理解する

処理の共通部分を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | inboxes_controller.rb | `app/controllers/public/api/v1/inboxes_controller.rb` | 基底コントローラー、認証処理 |

**主要処理フロー**:
- **12-16行目**: set_inbox_channel（APIチャネル取得）
- **18-22行目**: set_contact_inbox（contact_inbox取得）
- **24-28行目**: set_conversation（会話取得）

#### Step 3: 各リソースコントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | contacts_controller.rb | `app/controllers/public/api/v1/inboxes/contacts_controller.rb` | 連絡先CRUD |
| 3-2 | conversations_controller.rb | `app/controllers/public/api/v1/inboxes/conversations_controller.rb` | 会話CRUD、タイピング、ステータス |
| 3-3 | messages_controller.rb | `app/controllers/public/api/v1/inboxes/messages_controller.rb` | メッセージCRUD |

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

```
Public::Api::V1::InboxesController (基底)
    │
    ├─ set_inbox_channel
    │      └─ Channel::Api.find_by!(identifier:)
    │
    ├─ set_contact_inbox
    │      └─ inbox.contact_inboxes.find_by!(source_id:)
    │
    └─ set_conversation
           └─ contact.conversations.find_by!(display_id:)

Public::Api::V1::Inboxes::ContactsController
    │
    ├─ process_hmac
    │      └─ valid_hmac?
    │
    └─ ContactInboxWithContactBuilder / ContactIdentifyAction

Public::Api::V1::Inboxes::ConversationsController
    │
    ├─ index (hmac_verified判定で会話範囲決定)
    │
    ├─ create → ConversationBuilder
    │
    └─ toggle_status / toggle_typing / update_last_seen

Public::Api::V1::Inboxes::MessagesController
    │
    ├─ index → MessageFinder
    │
    ├─ create → build_attachment
    │
    └─ update (CSAT更新)
```

### データフロー図

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

inbox_id ─────────────▶ set_inbox_channel ────────────────▶ @inbox_channel
                              │
contact_id ───────────▶ set_contact_inbox ────────────────▶ @contact_inbox
                              │
identifier_hash ──────▶ process_hmac ─────────────────────▶ hmac_verified
                              │
                        各リソース処理 ───────────────────▶ @contact / @conversation / @message
                              │
                        JSON Response ────────────────────▶ クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| inboxes_controller.rb | `app/controllers/public/api/v1/inboxes_controller.rb` | ソース | 公開APIの基底コントローラー |
| contacts_controller.rb | `app/controllers/public/api/v1/inboxes/contacts_controller.rb` | ソース | 連絡先API |
| conversations_controller.rb | `app/controllers/public/api/v1/inboxes/conversations_controller.rb` | ソース | 会話API |
| messages_controller.rb | `app/controllers/public/api/v1/inboxes/messages_controller.rb` | ソース | メッセージAPI |
| api.rb | `app/models/channel/api.rb` | ソース | APIチャネルモデル |
| conversation_builder.rb | `app/builders/conversation_builder.rb` | ソース | 会話ビルダー |
| contact_identify_action.rb | `app/actions/contact_identify_action.rb` | ソース | 連絡先識別アクション |
| message_finder.rb | `app/finders/message_finder.rb` | ソース | メッセージ検索 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（509-526行目） |
