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

# Webhook

> Webhookを通じてJinba Toolboxからリアルタイムのイベント通知を受け取る

## 概要

Webhookを使用すると、組織内で重要なイベントが発生した際にHTTP通知を受け取ることができます。APIをポーリングする代わりに、URLを登録すると、Jinba Toolboxがリアルタイムでイベントペイロードをプッシュします。

Webhookは**組織ごと**に設定され、1つ以上のイベントタイプをサブスクライブできます。

## イベントタイプ

| イベント                 | 説明                       |
| -------------------- | ------------------------ |
| `tool.run.completed` | Toolの実行が正常に完了した          |
| `tool.run.failed`    | Toolの実行が失敗した             |
| `toolset.published`  | ToolSetの新しいVersionが公開された |
| `member.added`       | 組織に新しいメンバーが追加された         |
| `member.removed`     | 組織からメンバーが削除された           |

## Webhookの設定

<Steps>
  <Step title="Webhookエンドポイントを作成する">
    サーバー上にPOSTリクエストを受信できるHTTPエンドポイントを設定します。エンドポイントは受信確認として `2xx` ステータスコードを返す必要があります。
  </Step>

  <Step title="Webhookを登録する">
    APIまたはWebコンソールを使用して、エンドポイントURLを登録し、サブスクライブするイベントを選択します。

    ```bash theme={null}
    curl -X POST https://toolbox-api.jinba.dev/v1/orgs/{orgId}/webhooks \
      -H "Authorization: Bearer jtb_xxxxxxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/webhooks/jinba",
        "events": ["tool.run.completed", "tool.run.failed"]
      }'
    ```
  </Step>

  <Step title="署名シークレットを保存する">
    Webhookを作成すると、署名シークレットが生成されます。安全に保存してください。受信ペイロードの検証に使用します。
  </Step>

  <Step title="テストイベントを送信する">
    テストイベントを送信して、エンドポイントが正しく動作することを確認します：

    ```bash theme={null}
    curl -X POST https://toolbox-api.jinba.dev/v1/orgs/{orgId}/webhooks/{webhookId}/test \
      -H "Authorization: Bearer jtb_xxxxxxxxxxxx"
    ```
  </Step>
</Steps>

## Webhook設定

各Webhookには以下のプロパティがあります：

```typescript theme={null}
interface Webhook {
  id: string;
  orgId: string;
  url: string;           // 送信先URL
  events: string[];      // サブスクライブしたイベントタイプ
  secret: string;        // HMAC署名シークレット
  enabled: boolean;      // Webhookが有効かどうか
  createdAt: Date;
}
```

## ペイロード形式

すべてのWebhook配信は、以下の構造のJSONペイロードを送信します：

```json theme={null}
{
  "event": "tool.run.completed",
  "timestamp": "2026-02-01T12:00:00Z",
  "data": {
    "runId": "run_abc123",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "success",
    "durationMs": 1234
  }
}
```

### イベント固有のペイロード

**`tool.run.completed`**

```json theme={null}
{
  "event": "tool.run.completed",
  "timestamp": "2026-02-01T12:00:00Z",
  "data": {
    "runId": "run_abc123",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "success",
    "durationMs": 1234
  }
}
```

**`tool.run.failed`**

```json theme={null}
{
  "event": "tool.run.failed",
  "timestamp": "2026-02-01T12:05:00Z",
  "data": {
    "runId": "run_def456",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "failed",
    "error": {
      "name": "SlackAPIError",
      "message": "channel_not_found"
    },
    "durationMs": 567
  }
}
```

**`toolset.published`**

```json theme={null}
{
  "event": "toolset.published",
  "timestamp": "2026-02-01T10:00:00Z",
  "data": {
    "toolSetSlug": "slack-tools",
    "version": "1.3.0",
    "publishedBy": "user_abc123",
    "releaseNotes": "Added thread reply support"
  }
}
```

**`member.added`**

```json theme={null}
{
  "event": "member.added",
  "timestamp": "2026-02-01T09:00:00Z",
  "data": {
    "userId": "user_xyz789",
    "email": "newmember@example.com",
    "role": "member"
  }
}
```

## 署名の検証

すべてのWebhookリクエストには、Webhook署名シークレットを使用してリクエストボディから計算されたHMAC-SHA256署名を含む `X-Webhook-Signature` ヘッダーが付与されます。

```
X-Webhook-Signature: sha256=a1b2c3d4e5f6...
```

ペイロードが改ざんされていないことを確認するために、サーバー側で**署名を検証**してください：

```typescript theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// リクエストハンドラー内
const isValid = verifyWebhookSignature(
  requestBody,
  request.headers["x-webhook-signature"],
  process.env.WEBHOOK_SECRET!
);

if (!isValid) {
  return new Response("Invalid signature", { status: 401 });
}
```

## Webhook管理API

| メソッド   | エンドポイント                             | 説明           |
| ------ | ----------------------------------- | ------------ |
| GET    | `/v1/orgs/:orgId/webhooks`          | Webhook一覧の取得 |
| POST   | `/v1/orgs/:orgId/webhooks`          | Webhookの作成   |
| PATCH  | `/v1/orgs/:orgId/webhooks/:id`      | Webhookの更新   |
| DELETE | `/v1/orgs/:orgId/webhooks/:id`      | Webhookの削除   |
| POST   | `/v1/orgs/:orgId/webhooks/:id/test` | テストイベントの送信   |

## リトライ動作

エンドポイントが `2xx` ステータスコードを返さない場合、Jinba Toolboxは配信をリトライします：

| 試行       | 遅延  |
| -------- | --- |
| 1回目のリトライ | 1分  |
| 2回目のリトライ | 5分  |
| 3回目のリトライ | 30分 |

3回連続で失敗すると、Webhookは自動的に**無効化**されます。根本的な問題が解決されたら、WebコンソールまたはAPIから手動で再有効化できます。

## ベストプラクティス

* **ペイロードを処理する前に必ず署名を検証する** -- 改ざんを防止するためです。
* **`200` ステータスを速やかに返す** -- Webhookの確認応答後に、重い処理は非同期で実行してください。
* **冪等な処理を実装する** -- リトライにより、Webhook配信が複数回届く可能性があります。`runId` やイベントタイムスタンプを使用して重複排除してください。
* **Webhookの健全性を監視する** -- 無効化されたWebhookは速やかに再有効化し、配信失敗を調査してください。
* **必要なイベントのみサブスクライブする** -- 統合に必要なイベントだけを選択して、ノイズを減らしてください。

## 関連ドキュメント

* [REST API](/ja/pages/toolbox/developer/api) -- 完全なエンドポイントリファレンス
* [セキュリティとアクセス制御](/ja/pages/toolbox/advanced/security) -- 認証と認可の詳細
