> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Cloud Agents のライフサイクルイベントを HTTP Webhook で購読します。エンドポイント API、配信と署名検証、および完全なイベントカタログを解説します。

## 概要

Webhook は Qoder Cloud Agents が提供するイベント駆動型のプッシュ機構です。Agent や Session などのリソースにライフサイクルの変化が生じると、システムは構造化されたイベントを HTTP POST で開発者が登録した URL に配信します。ポーリングは不要です。

**主な特徴:**

* **イベント駆動型プッシュ** — リソースの状態変化時に能動的に通知し、クライアント側のポーリングは不要
* **envelope 構造** — 統一された `BetaWebhookEvent { id, created_at, data, type:"event" }` フォーマット
* **配信セマンティクス** — 少なくとも 1 回(at-least-once)の保証、HMAC-SHA256 署名検証、指数バックオフによるリトライ
* **ワイルドカード購読** — `*` を使用してすべてのイベントタイプを購読でき、連携を簡素化

**ユースケース:**

| シナリオ                 | 説明                                  |
| -------------------- | ----------------------------------- |
| 非同期タスク完了通知           | Session の完了時に下流のワークフローをトリガーする       |
| Agent 設定の監査          | Agent の作成/更新/削除操作を追跡する              |
| マルチ Agent オーケストレーション | Thread の状態変化に応じてサブタスクのスケジューリングを駆動する |
| 運用監視とアラート            | 連続失敗回数がしきい値を超えたときにアラートを発する          |

***

## ドメインタイプ

このセクションでは Webhook イベントのデータ構造を定義します。各イベントタイプの `data` フィールドは、リソース ID、イベントタイプ、およびイベント固有の追加フィールドを含む統一されたオブジェクト形式に従います。

***

### Session ライフサイクルイベント

***

### Webhook Session Created Event Data

* `WebhookSessionCreatedEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.created"`

    * `"session.created"`

    Session が正常に作成されたときにトリガーされます。システムは `POST /sessions` により Session が作成された直後にこのイベントを送信します。

***

### Webhook Session Updated Event Data

* `WebhookSessionUpdatedEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.updated"`

    * `"session.updated"`

    Session のメタデータ(タイトルや metadata など)が変更されたときにトリガーされます。

***

### Webhook Session Archived Event Data

* `WebhookSessionArchivedEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.archived"`

    * `"session.archived"`

    Session がアーカイブされたときにトリガーされます。アーカイブ後、Session は新しいメッセージを受け付けなくなりますが、履歴データは引き続き照会可能です。

***

### Webhook Session Deleted Event Data

* `WebhookSessionDeletedEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.deleted"`

    * `"session.deleted"`

    Session が永久に削除されたときにトリガーされます。削除後、Session のすべてのデータは復元できなくなります。

***

### Session ステータスイベント

***

### Webhook Session Status Run Started Event Data

* `WebhookSessionStatusRunStartedEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.status_run_started"`

    * `"session.status_run_started"`

    Agent が実行を開始したときにトリガーされます。Session が実行状態に入り、ユーザーの指示を実行していることを示します。

***

### Webhook Session Status Idled Event Data

* `WebhookSessionStatusIdledEventData object { id, type }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.status_idled"`

    * `"session.status_idled"`

    ターンが完了し、Session がアイドル状態に戻ったときにトリガーされます。この時点で Session の最新の出力を安全に読み取ることができます。

***

### Session Thread イベント

マルチ Agent 協調シナリオに適用されます。Thread イベントは基本フィールドに加えて `session_thread_id` フィールドを持ち、特定の実行スレッドを識別します。

***

### Webhook Session Thread Created Event Data

* `WebhookSessionThreadCreatedEventData object { id, type, session_thread_id }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.thread_created"`

    * `"session.thread_created"`

    新しい Session Thread が作成されたときにトリガーされます。メイン Agent がタスクを実行するために子スレッドを生成する、サブ Agent 協調シナリオでよく見られます。

  * `session_thread_id: string`

    関連付けられた Session Thread ID。

***

### Webhook Session Thread Idled Event Data

* `WebhookSessionThreadIdledEventData object { id, type, session_thread_id }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.thread_idled"`

    * `"session.thread_idled"`

    Session Thread が実行を終了してアイドル状態に入ったときにトリガーされます。スレッドが現在のタスクを完了し、結果を読み取れることを示します。

  * `session_thread_id: string`

    関連付けられた Session Thread ID。

***

### Webhook Session Thread Terminated Event Data

* `WebhookSessionThreadTerminatedEventData object { id, type, session_thread_id }`

  * `id: string`

    イベントをトリガーした Session ID。

  * `type: "session.thread_terminated"`

    * `"session.thread_terminated"`

    Session Thread が終了(terminate)されたときにトリガーされます。終了された Thread は復元できません。作業を続けるには新しい Thread を作成する必要があります。

  * `session_thread_id: string`

    関連付けられた Session Thread ID。

***

### Agent ライフサイクルイベント

Agent イベントは基本フィールドに加えて `version` フィールドを持ち、Agent の設定バージョン番号を示します。

***

### Webhook Agent Created Event Data

* `WebhookAgentCreatedEventData object { id, type, version }`

  * `id: string`

    イベントをトリガーした Agent ID。

  * `type: "agent.created"`

    * `"agent.created"`

    Agent が正常に作成されたときにトリガーされます。システムは `POST /agents` により Agent が作成された後にこのイベントを送信します。

  * `version: integer`

    Agent のバージョン番号。初回作成時は `1` に設定されます。

***

### Webhook Agent Updated Event Data

* `WebhookAgentUpdatedEventData object { id, type, version }`

  * `id: string`

    イベントをトリガーした Agent ID。

  * `type: "agent.updated"`

    * `"agent.updated"`

    Agent の設定が更新されたときにトリガーされます。`version` は更新のたびにインクリメントされます。

  * `version: integer`

    Agent のバージョン番号。

***

### Webhook Agent Archived Event Data

* `WebhookAgentArchivedEventData object { id, type, version }`

  * `id: string`

    イベントをトリガーした Agent ID。

  * `type: "agent.archived"`

    * `"agent.archived"`

    Agent がアーカイブされたときにトリガーされます。アーカイブ後、Agent は新しい Session 作成リクエストを受け付けなくなります。

  * `version: integer`

    Agent のバージョン番号。

***

### Webhook Agent Deleted Event Data

* `WebhookAgentDeletedEventData object { id, type, version }`

  * `id: string`

    イベントをトリガーした Agent ID。

  * `type: "agent.deleted"`

    * `"agent.deleted"`

    Agent が削除されたときにトリガーされます。削除後、Agent のすべての設定データは復元できなくなります。

  * `version: integer`

    Agent のバージョン番号。

***

## Webhook エンドポイント API

Webhook エンドポイントを管理するための CRUD エンドポイントです。これらを使用して、Webhook エンドポイントの作成、照会、更新、削除を行うほか、テストイベントの送信やエンドポイントの有効化/無効化の制御を行います。

**Base URL:**

| リージョン  | アドレス                                    |
| ------ | --------------------------------------- |
| Global | `https://api.qoder.com/api/v1/cloud`    |
| CN     | `https://api.qoder.com.cn/api/v1/cloud` |

**認証:**

すべてのエンドポイントでは、リクエストヘッダーに Personal Access Token が必要です:

```
Authorization: Bearer $PAT
```

***

### POST /webhook\_endpoints

新しい Webhook エンドポイントを作成します。

**リクエストパラメータ:**

| フィールド         | 型         | 必須  | 説明                                                 |
| ------------- | --------- | --- | -------------------------------------------------- |
| `url`         | string    | はい  | イベント配信を受信する HTTP または HTTPS URL（本番環境では HTTPS を強く推奨） |
| `description` | string    | いいえ | 管理目的のエンドポイント説明                                     |
| `events`      | string\[] | はい  | 購読するイベントタイプのリスト。`*` ワイルドカードに対応                     |

**レスポンス:** `201 Created`

```json theme={null}
{
  "id": "5fc05310-4d9c-447e-a4b8-f124f17e1ff0",
  "url": "https://webhook.site/your-unique-path",
  "description": "quickstart demo",
  "events": ["*"],
  "active": true,
  "signing_secret": "whsec_BfJDodFEzmkdjAUf19-_XthSq6KbPKmRjfm9KFWMIuk",
  "signing_secret_version": 1,
  "created_at": "2026-07-02T18:17:30.069191+08:00"
}
```

> **Note:** `signing_secret` は作成時に一度だけ返されます。安全に保管してください。以降の照会系エンドポイントではこのフィールドは返されません。

**ステータスコード:**

| コード | 説明                                    |
| --- | ------------------------------------- |
| 201 | 作成成功                                  |
| 400 | 無効なリクエストパラメータ(URL の形式不正、無効なイベントタイプなど) |
| 401 | 認証エラー、無効または期限切れのトークン                  |
| 422 | セマンティックエラー(URL の重複登録など)               |

**例:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://webhook.site/your-unique-path",
    "description": "quickstart demo",
    "events": ["*"]
  }'
```

***

### GET /webhook\_endpoints

現在のアカウント配下のすべての Webhook エンドポイントを一覧表示します。

**リクエストパラメータ:** なし

**レスポンス:** `200 OK`

```json theme={null}
{
  "data": [
    {
      "id": "700e7789-edab-41cb-b60a-210d0df38ab6",
      "url": "https://myapp.example.com/hooks/qoder",
      "description": "prod app hook",
      "events": ["session.created", "session.thread_idled"],
      "active": true,
      "signing_secret_version": 1,
      "consecutive_fail": 0,
      "last_success_at": "2026-07-02T18:08:12.666783+08:00",
      "created_at": "2026-07-02T17:47:16.073822+08:00",
      "updated_at": "2026-07-02T17:47:16.073822+08:00"
    }
  ]
}
```

**レスポンスフィールド:**

| フィールド                    | 型         | 説明                                     |
| ------------------------ | --------- | -------------------------------------- |
| `id`                     | string    | エンドポイントの一意識別子                          |
| `url`                    | string    | イベントを受信する URL                          |
| `description`            | string    | エンドポイントの説明                             |
| `events`                 | string\[] | 購読中のイベントタイプ                            |
| `active`                 | boolean   | 有効かどうか                                 |
| `signing_secret_version` | integer   | 署名シークレットのバージョン番号                       |
| `consecutive_fail`       | integer   | 連続配信失敗回数                               |
| `last_success_at`        | string    | 最後に配信が成功した時刻(RFC 3339)                 |
| `last_failure_at`        | string    | 最後に配信が失敗した時刻(RFC 3339)。失敗記録がない場合は null |
| `created_at`             | string    | 作成時刻(RFC 3339)                         |
| `updated_at`             | string    | 最終更新時刻(RFC 3339)                       |

**例:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_endpoints/\{id}

特定の Webhook エンドポイントの詳細を取得します。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**レスポンス:** `200 OK`

一覧エンドポイントの要素と同じ構造の単一のエンドポイントオブジェクトを返します。

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 200 | 成功             |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT"
```

***

### PUT /webhook\_endpoints/\{id}

特定の Webhook エンドポイントの設定を更新します。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**リクエストパラメータ:**

| フィールド         | 型         | 必須  | 説明               |
| ------------- | --------- | --- | ---------------- |
| `url`         | string    | いいえ | イベント配信 URL を更新する |
| `description` | string    | いいえ | エンドポイント説明を更新する   |
| `events`      | string\[] | いいえ | 購読するイベントタイプを更新する |

**レスポンス:** `200 OK`

更新後の完全なエンドポイントオブジェクトを返します。

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 200 | 更新成功           |
| 400 | 無効なリクエストパラメータ  |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl -X PUT https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["session.created", "session.status_idled", "agent.updated"],
    "description": "updated prod hook"
  }'
```

***

### DELETE /webhook\_endpoints/\{id}

Webhook エンドポイントを永久に削除します。未配信のイベントはすべて破棄されます。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**レスポンス:** `204 No Content`

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 204 | 削除成功           |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl -X DELETE https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/test

指定したエンドポイントにテストイベントを送信し、接続性と署名検証ロジックを確認します。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**リクエストパラメータ:** なし

**レスポンス:** `202 Accepted`

```json theme={null}
{"event_id": 433, "delivery_rows": 2}
```

**レスポンスフィールド:**

| フィールド           | 型       | 説明                      |
| --------------- | ------- | ----------------------- |
| `event_id`      | integer | テストイベントの内部 ID           |
| `delivery_rows` | integer | イベントが公開されたメッセージパーティション数 |

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 202 | テストイベント送信済み    |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/test \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/enable

無効化された Webhook エンドポイントを有効化します。有効化されると、エンドポイントはイベント配信の受信を再開します。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**リクエストパラメータ:** なし

**レスポンス:** `200 OK`

有効化後のエンドポイントオブジェクト(`active: true`)を返します。

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 200 | 有効化成功          |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/enable \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/disable

Webhook エンドポイントを無効化します。無効化されると、エンドポイントはイベント配信の受信を停止しますが、削除はされません。

**パスパラメータ:**

| パラメータ | 型      | 説明                 |
| ----- | ------ | ------------------ |
| `id`  | string | Webhook エンドポイント ID |

**リクエストパラメータ:** なし

**レスポンス:** `200 OK`

無効化後のエンドポイントオブジェクト(`active: false`)を返します。

**ステータスコード:**

| コード | 説明             |
| --- | -------------- |
| 200 | 無効化成功          |
| 404 | エンドポイントが見つからない |

**例:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/disable \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_events

監査やトラブルシューティングのために Webhook イベント配信レコードを一覧表示します。

**リクエストパラメータ:** なし

**レスポンス:** `200 OK`

配信ステータス、タイムスタンプなどの情報を含むイベント一覧を返します。

**例:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_events \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_events/\{id}

単一の Webhook イベントの詳細情報を取得します。

**パスパラメータ:**

| パラメータ | 型      | 説明              |
| ----- | ------ | --------------- |
| `id`  | string | Webhook イベント ID |

**レスポンス:** `200 OK`

**ステータスコード:**

| コード | 説明          |
| --- | ----------- |
| 200 | 成功          |
| 404 | イベントが見つからない |

**例:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_events/433 \
  -H "Authorization: Bearer $PAT"
```

***

### エラーレスポンス形式

すべてのエンドポイントは、エラー発生時に統一されたエラー構造を返します:

```json theme={null}
{
  "error": {
    "message": "Unknown event type: agent.updated",
    "type": "invalid_request_error"
  },
  "request_id": "cc865161-b25d-4d68-bad5-bf0363f6e0f9",
  "type": "error"
}
```

**エラーフィールド:**

| フィールド           | 型      | 説明                                                    |
| --------------- | ------ | ----------------------------------------------------- |
| `error.message` | string | 人間が読めるエラー説明                                           |
| `error.type`    | string | エラーカテゴリ(例: `invalid_request_error`、`not_found_error`) |
| `request_id`    | string | サポートチームでのトラブルシューティング用のリクエストトレース ID                    |
| `type`          | string | 固定値 `"error"`                                         |

> **注意:** 認証層(Token が無効または欠落している場合など)が返す 401 レスポンスは、ゲートウェイから直接返されるため、上記の業務エラー構造に従わない場合があります。

***

## Webhook 配信

### 配信方法

システムは、以下の形式で HTTP POST により登録済み URL にイベントを配信します:

* **Method:** `POST`
* **Content-Type:** `application/json`
* **Body:** JSON envelope 構造（[envelope 構造](#envelope-構造)を参照）

### リクエストヘッダー

各配信には以下の HTTP ヘッダーが含まれます:

| ヘッダー                | 説明                                                       |
| ------------------- | -------------------------------------------------------- |
| `Content-Type`      | `application/json`                                       |
| `User-Agent`        | `QoderCloudAgents-Webhook/1.0`                           |
| `Webhook-Signature` | `t=<unix_epoch>,v1=<hmac_sha256_hex>` 形式の HMAC-SHA256 署名 |

### 署名検証

イベントの真正性とデータの完全性を確保するため、各配信には HMAC-SHA256 署名が付与されます。開発者はイベントを処理する前に署名を検証する必要があります。

**署名形式:**

```
t=1719907336,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8f9
```

**署名アルゴリズム:**

```
signature = HMAC_SHA256(signing_secret, "<t>.<raw_body>")
```

ここで:

* `signing_secret` — エンドポイント作成時に返されるシークレット(`whsec_` プレフィックス)
* `t` — 署名時の Unix タイムスタンプ(秒)
* `raw_body` — リクエストボディの生のバイト内容(パース前)

**検証手順:**

1. `Webhook-Signature` ヘッダーから `t` と `v1` を抽出する
2. リプレイ攻撃を防ぐため、タイムスタンプが許容ウィンドウ内(推奨 600 秒)であることを確認する
3. `signing_secret` を使用して `"<t>.<raw_body>"` の HMAC-SHA256 を計算する
4. 結果を `v1` とタイミングセーフな比較で照合する

### 署名検証のコード例

**Node.js:**

```js theme={null}
const crypto = require('node:crypto');

function verifyWebhook(secret, rawBody, header, toleranceSec = 600) {
  const parts = Object.fromEntries(
    header.split(',').map(kv => kv.split('=').map(s => s.trim()))
  );
  const ts = parseInt(parts.t, 10);
  if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(parts.v1, 'hex')
  );
}
```

**Python:**

```python theme={null}
import hmac, hashlib, time

def verify_webhook(secret: bytes, raw_body: bytes, header: str, tolerance_sec: int = 600) -> bool:
    parts = dict(kv.split("=", 1) for kv in header.split(","))
    ts = int(parts["t"])
    if abs(time.time() - ts) > tolerance_sec:
        return False
    signed_payload = f"{ts}.".encode() + raw_body
    expected = hmac.new(secret, signed_payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
```

**Go:**

```go theme={null}
func verifyWebhook(secret, rawBody []byte, header string, toleranceSec int64) bool {
    var ts int64; var v1 string
    for _, kv := range strings.Split(header, ",") {
        kv = strings.TrimSpace(kv)
        if strings.HasPrefix(kv, "t=") { ts, _ = strconv.ParseInt(kv[2:], 10, 64) }
        if strings.HasPrefix(kv, "v1=") { v1 = kv[3:] }
    }
    if ts == 0 || v1 == "" { return false }
    if diff := time.Now().Unix() - ts; diff > toleranceSec || diff < -toleranceSec { return false }
    mac := hmac.New(sha256.New, secret)
    fmt.Fprintf(mac, "%d.", ts)
    mac.Write(rawBody)
    return hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(v1))
}
```

### リトライ戦略

配信が失敗した場合、システムは指数バックオフでリトライを行います:

| 試行   | 遅延   | 説明        |
| ---- | ---- | --------- |
| 1 回目 | 即時   | 初回配信      |
| 2 回目 | 1 秒  | 1 回目のリトライ |
| 3 回目 | 5 秒  | 2 回目のリトライ |
| 4 回目 | 30 秒 | 最後のリトライ   |

合計 4 回の試行(1 回の配信 + 3 回のリトライ)。すべて失敗した後、イベントはデッドレターキューに入ります。

### レスポンスコードの処理

| レスポンスコード範囲 | 処理                                             |
| ---------- | ---------------------------------------------- |
| 2xx        | 配信成功、イベントは配信済みとしてマークされる                        |
| 4xx        | リトライなし、イベントは破棄済みとしてマークされる(クライアントエラーは開発者が修正すべき) |
| 5xx        | リトライをトリガーする(一時的なサーバー障害)                        |
| Timeout    | リトライをトリガーする(デフォルトのタイムアウトは 30 秒)                |

### 自動デグレード

エンドポイントの `consecutive_fail` カウントが **20** を超えると、システムはデグレード警告をトリガーします。開発者はこのメトリクスを監視し、失敗が継続する場合は以下を確認してください:

* エンドポイント URL が到達可能か
* SSL 証明書が有効か
* サーバーが正常に応答しているか
* 署名検証ロジックにバグがないか

***

## サポート対象のイベントタイプ(完全な一覧)

### イベントタイプ概要

| イベントタイプ                                   | ステータス        | 追加 data フィールド       | トリガー条件                          |
| ----------------------------------------- | ------------ | ------------------- | ------------------------------- |
| `session.created`                         | ✅ 実装済み       | —                   | POST /sessions が Session を正常に作成 |
| `session.updated`                         | ✅ 実装済み       | —                   | Session メタデータが変更された             |
| `session.archived`                        | ✅ 実装済み       | —                   | Session がアーカイブされた               |
| `session.deleted`                         | ✅ 実装済み       | —                   | Session が永久に削除された               |
| `session.status_run_started`              | ✅ 実装済み       | —                   | Agent が実行を開始                    |
| `session.status_idled`                    | ✅ 実装済み       | —                   | ターン完了、アイドルに復帰                   |
| `session.thread_created`                  | ✅ 実装済み       | `session_thread_id` | 新しい Session Thread が作成された       |
| `session.thread_idled`                    | ✅ 実装済み       | `session_thread_id` | Thread の実行が終了、アイドルに移行           |
| `session.thread_terminated`               | ✅ 実装済み       | `session_thread_id` | Thread が終了された                   |
| `agent.created`                           | ✅ 実装済み       | `version`           | POST /agents が Agent を作成        |
| `agent.updated`                           | ✅ 実装済み       | `version`           | Agent 設定が更新された                  |
| `agent.archived`                          | ✅ 実装済み       | `version`           | Agent がアーカイブされた                 |
| `agent.deleted`                           | ✅ 実装済み       | `version`           | Agent が削除された                    |
| `session.pending`                         | 🔜 許可リスト登録済み | —                   | Session がスケジューリング待ち             |
| `session.running`                         | 🔜 許可リスト登録済み | —                   | Session が実行中                    |
| `session.idled`                           | 🔜 許可リスト登録済み | —                   | Session がアイドルに移行                |
| `session.requires_action`                 | 🔜 許可リスト登録済み | —                   | Session が人的介入を必要とする             |
| `session.status_run_failed`               | 🔜 許可リスト登録済み | —                   | 実行が失敗した                         |
| `session.status_terminated`               | 🔜 許可リスト登録済み | —                   | Session が終了された                  |
| `session.status_rescheduled`              | 🔜 許可リスト登録済み | —                   | Session が再スケジュールされた             |
| `session.status_paused_pending_input`     | 🔜 許可リスト登録済み | —                   | ユーザー入力待ちで一時停止                   |
| `session.status_paused_pending_approval`  | 🔜 許可リスト登録済み | —                   | 承認待ちで一時停止                       |
| `session.status_paused_user_intervention` | 🔜 許可リスト登録済み | —                   | 人的介入待ちで一時停止                     |
| `session.outcome_evaluation_started`      | 🔜 許可リスト登録済み | —                   | 成果評価が開始された                      |
| `session.outcome_evaluation_ended`        | 🔜 許可リスト登録済み | —                   | 成果評価が終了した                       |
| `vault.created`                           | 🔜 許可リスト登録済み | —                   | Vault が作成された                    |
| `vault.deleted`                           | 🔜 許可リスト登録済み | —                   | Vault が削除された                    |
| `vault.updated`                           | 🔜 許可リスト登録済み | —                   | Vault が更新された                    |
| `vault_credential.created`                | 🔜 許可リスト登録済み | —                   | 認証情報が作成された                      |
| `vault_credential.updated`                | 🔜 許可リスト登録済み | —                   | 認証情報が更新された                      |
| `vault_credential.deleted`                | 🔜 許可リスト登録済み | —                   | 認証情報が削除された                      |
| `vault_credential.shared`                 | 🔜 許可リスト登録済み | —                   | 認証情報が共有された                      |
| `vault_credential.revoked`                | 🔜 許可リスト登録済み | —                   | 認証情報が取り消された                     |
| `deployment.created`                      | 📋 計画中       | —                   | Deployment が作成された               |
| `deployment.updated`                      | 📋 計画中       | —                   | Deployment が更新された               |
| `deployment.archived`                     | 📋 計画中       | —                   | Deployment がアーカイブされた            |
| `deployment.deleted`                      | 📋 計画中       | —                   | Deployment が削除された               |
| `deployment.paused`                       | 📋 計画中       | —                   | Deployment が一時停止された             |
| `deployment.unpaused`                     | 📋 計画中       | —                   | Deployment が再開された               |
| `deployment_run.started`                  | 📋 計画中       | —                   | Deployment 実行が開始された             |
| `deployment_run.failed`                   | 📋 計画中       | —                   | Deployment 実行が失敗した              |
| `deployment_run.succeeded`                | 📋 計画中       | —                   | Deployment 実行が成功した              |
| `environment.created`                     | 📋 計画中       | —                   | Environment が作成された              |
| `environment.updated`                     | 📋 計画中       | —                   | Environment が更新された              |
| `environment.archived`                    | 📋 計画中       | —                   | Environment がアーカイブされた           |
| `environment.deleted`                     | 📋 計画中       | —                   | Environment が削除された              |
| `memory_store.created`                    | 📋 計画中       | —                   | Memory store が作成された             |
| `memory_store.archived`                   | 📋 計画中       | —                   | Memory store がアーカイブされた          |
| `memory_store.deleted`                    | 📋 計画中       | —                   | Memory store が削除された             |

### 特殊なイベントタイプ

| イベントタイプ        | 説明                                                                     |
| -------------- | ---------------------------------------------------------------------- |
| `*`            | ワイルドカード — すべてのイベントタイプを購読します。すべてのイベントを受信するには、エンドポイント作成時に `["*"]` を使用します |
| `webhook.test` | 内部テストイベント。`POST /webhook_endpoints/{id}/test` を介してトリガーされます             |

### ステータスラベル

| ラベル          | 意味                                                             |
| ------------ | -------------------------------------------------------------- |
| ✅ 実装済み       | システム内に完全なイベントトリガーポイントが存在し、正常に購読・受信できる                          |
| 🔜 許可リスト登録済み | イベントタイプが検証用の許可リストに登録済み(`events` フィールドで使用可能)だが、トリガーポイントはまだ存在しない |
| 📋 計画中       | API 設計レベルで定義されているが、まだ許可リストに登録されていないイベントタイプ                     |

***

## envelope 構造

すべての Webhook イベントは、統一された envelope 構造にラップされて配信されます。

### 基本 envelope 構造

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:16Z",
  "type": "event",
  "data": {
    "id": "sess_019f224773fe71d79c5869bd089d159c",
    "type": "session.created"
  }
}
```

### Thread イベントの envelope

Thread イベントは `data` 内に追加の `session_thread_id` フィールドを含みます:

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:17Z",
  "type": "event",
  "data": {
    "id": "sess_019f224773fe71d79c5869bd089d159c",
    "type": "session.thread_idled",
    "session_thread_id": "sthread_019f224..."
  }
}
```

### Agent イベントの envelope

Agent イベントは `data` 内に追加の `version` フィールドを含みます:

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:16Z",
  "type": "event",
  "data": {
    "id": "agent_019f224773fe71d7...",
    "type": "agent.created",
    "version": 1
  }
}
```

### フィールドの説明

| フィールド                    | 型       | 説明                                                    |
| ------------------------ | ------- | ----------------------------------------------------- |
| `id`                     | string  | イベントの一意識別子。形式: `whevt_` + 一意識別子。イベントの冪等性キーから決定的に生成される |
| `created_at`             | string  | イベント作成時刻、RFC 3339 UTC 形式                              |
| `type`                   | string  | 固定値 `"event"`、これがイベント envelope であることを示す               |
| `data`                   | object  | トリガーとなったリソース情報を含むイベントペイロード                            |
| `data.id`                | string  | イベントをトリガーしたリソースの ID(Session ID または Agent ID)          |
| `data.type`              | string  | イベントタイプ文字列(例: `"session.created"`、`"agent.updated"`)  |
| `data.session_thread_id` | string  | (Thread イベントのみ)関連付けられた Session Thread ID              |
| `data.version`           | integer | (Agent イベントのみ)Agent の設定バージョン番号                        |

### 冪等性の処理

envelope 内の `id` フィールドは重複排除キーとして使用できます。配信セマンティクスは少なくとも 1 回(at-least-once)であるため、同じイベントが複数回配信される可能性があります。受信側は以下を行うべきです:

1. `id` を重複排除の一意キーとして使用する
2. 処理前に `id` がすでに消費済みかどうかを確認する
3. イベント処理ロジックが冪等であることを保証する

***

## 計画中のイベント(近日提供予定)

以下のイベントタイプは API 設計レベルで整合済みで、それぞれのリソース機能の提供開始に合わせてロールアウトされます。具体的な時期は製品要件によって異なります。

### deployment.\*(Deployment ライフサイクル、6 イベント)

| イベントタイプ               | トリガー条件                             |
| --------------------- | ---------------------------------- |
| `deployment.created`  | 新しい Deployment が作成された              |
| `deployment.updated`  | Deployment 設定が更新された                |
| `deployment.archived` | Deployment がアーカイブされた               |
| `deployment.deleted`  | Deployment が削除された                  |
| `deployment.paused`   | Deployment が一時停止された(新しい実行のトリガーを停止) |
| `deployment.unpaused` | Deployment が再開された(実行のトリガーを再開)      |

### deployment\_run.\*(Deployment 実行ステータス、3 イベント)

| イベントタイプ                    | トリガー条件                |
| -------------------------- | --------------------- |
| `deployment_run.started`   | Deployment 実行が実行を開始する |
| `deployment_run.failed`    | Deployment 実行が失敗した    |
| `deployment_run.succeeded` | Deployment 実行が正常に完了した |

### environment.\*(Environment 管理、4 イベント)

| イベントタイプ                | トリガー条件                   |
| ---------------------- | ------------------------ |
| `environment.created`  | ランタイム Environment が作成された |
| `environment.updated`  | Environment 設定が更新された     |
| `environment.archived` | Environment がアーカイブされた    |
| `environment.deleted`  | Environment が削除された       |

### memory\_store.\*(Memory Store、3 イベント)

| イベントタイプ                 | トリガー条件                    |
| ----------------------- | ------------------------- |
| `memory_store.created`  | Memory Store インスタンスが作成された |
| `memory_store.archived` | Memory Store がアーカイブされた    |
| `memory_store.deleted`  | Memory Store が削除された       |

### vault.\*(Vault、3 イベント)

| イベントタイプ         | トリガー条件         |
| --------------- | -------------- |
| `vault.created` | Vault が作成された   |
| `vault.updated` | Vault 設定が更新された |
| `vault.deleted` | Vault が削除された   |

### vault\_credential.\*(認証情報管理、5 イベント)

| イベントタイプ                    | トリガー条件               |
| -------------------------- | -------------------- |
| `vault_credential.created` | Vault に新しい認証情報が追加された |
| `vault_credential.updated` | 認証情報が更新された           |
| `vault_credential.deleted` | 認証情報が削除された           |
| `vault_credential.shared`  | 認証情報が他の Agent と共有された |
| `vault_credential.revoked` | 認証情報の共有権限が取り消された     |

***

## 付録 A: クイックスタートガイド

### ステップ 1: Webhook エンドポイントを作成する

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/qoder",
    "description": "Production webhook",
    "events": ["session.status_idled", "session.thread_idled"]
  }'
```

### ステップ 2: signing\_secret を保存する

作成レスポンスから `signing_secret` を取得し、後の署名検証のために安全に保管します。

### ステップ 3: 受信側を実装する

サービス内に Webhook 受信エンドポイントを実装し、以下を確実に行ってください:

1. `Webhook-Signature` 署名を検証する
2. イベントの `id` を使用して冪等な重複排除を行う
3. 受信を確認するために `200 OK` を返す
4. ビジネスロジックを非同期に処理する(タイムアウトを回避する)

### ステップ 4: テストイベントを送信する

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/{id}/test \
  -H "Authorization: Bearer $PAT"
```

### ステップ 5: 検証して本番稼働する

テストイベントが正しく受信されることを確認したら、本番運用の準備が整います。

***

## 付録 B: ベストプラクティス

| プラクティス    | 説明                                          |
| --------- | ------------------------------------------- |
| 署名検証      | 常に `Webhook-Signature` を検証し、無効なリクエストを拒否する   |
| 冪等処理      | イベントの `id` を使用して重複排除を行い、重複消費を防ぐ             |
| 高速応答      | 受信側は 5 秒以内に 2xx を返し、時間のかかるロジックは非同期に実行する     |
| タイムスタンプ検証 | 許容ウィンドウ外のイベントを拒否し、リプレイ攻撃を防ぐ                 |
| 正確な購読     | 必要なイベントタイプのみを購読し、不要なネットワークオーバーヘッドを削減する      |
| 監視とアラート   | `consecutive_fail` メトリクスを監視し、配信の異常を速やかに検知する |
