# Webhook 通知

Takumi API では、診断などのワークフロー完了時に Webhook で通知を受け取ることができます。これにより、ポーリングを行わなくてもワークフローの完了を検知し、結果の取得や後続処理を自動的に開始できます。

## ワークフロー完了イベント

ワークフローの実行が完了すると、指定した Webhook エンドポイントに以下の形式で HTTP POST リクエストが送信されます。イベントの `type` は `api.workflow_run.exited` です。

```json
{
  "type": "api.workflow_run.exited",
  "timestamp": "2000-01-01T00:00:00Z",
  "data": {
    "workflow_id": "whitebox-assessment",
    "workflow_run_id": "TWR..."
  }
}
```

`data.workflow_run_id` を使ってワークフロー実行を特定し、`/o/{org_id}/workflows/{workflow_id}/describe` エンドポイントで結果を取得できます。

## Webhook エンドポイントの指定方法

ワークフロー実行 API (`/o/{org_id}/workflows/{workflow_id}/dispatch`) のリクエストボディで `notification.webhook_endpoint_ids` にエンドポイント ID を指定すると、そのワークフロー完了時に通知が送られます。

```js
{
  "input": { /* ... */ },
  "notification": {
    "webhook_endpoint_ids": ["WH..."]
  }
}
```

## Webhook の仕様

イベントが発生した場合に、指定された Webhook エンドポイントに対して HTTP POST リクエストが送信されます。

リクエストボディは以下の構造を持ちます。

```js
{
  "type": "{イベント種別}",
  "timestamp": "2024-01-01T00:00:00Z",
  "data": 
}
```

Webhook エンドポイントは **10 秒以内**に HTTP **2xx** ステータスコードを返すことで、リクエストの受信成功を示す必要があります。ステータスコードが 3xx の場合は失敗として扱われ、リダイレクト先にリクエストは送られません。

リクエスト送信に失敗した場合（非 2xx レスポンス、タイムアウト、ネットワークエラーなど）、本サービスは自動的にリトライを行います。

Webhook の配送は **at-least-once** であり、同一イベントが複数回配送される場合があります。各リクエストには `webhook-id` ヘッダーが付与されており、同一の `webhook-id` を持つリクエストは同一イベントの再送です。エンドポイントは `webhook-id` を用いて冪等に実装することをお勧めします。

### 署名の検証

受信したリクエストが確かに本サービスから送信されたものであることを検証するため、受信時に必ず署名を検証してください。
本サービスは [Standard Webhooks](https://www.standardwebhooks.com/) 仕様に準拠したリクエストの署名方式を採用しています。

以下は TypeScript での実装例です。

```typescript
import { Webhook } from "standardwebhooks";

const wh = new Webhook(signingSecret);

// リクエストヘッダーと本文を渡して署名を検証する
wh.verify(requestBody, {
  "webhook-id": req.headers.get("webhook-id"),
  "webhook-timestamp": req.headers.get("webhook-timestamp"),
  "webhook-signature": req.headers.get("webhook-signature"),
});

const payload = JSON.parse(requestBody);
```

## Webhook エンドポイントをセットアップする {#setup-endpoints}

### Webhook エンドポイントを登録する

[設定 > Webhook エンドポイント](https://cloud.shisho.dev/*/settings/webhooks) を開き、**「新しい Webhook エンドポイントを登録」** から受信先の URL と説明を入力して登録します。

登録が完了すると、**エンドポイント ID**（`WH...` 形式）と**署名シークレット**が表示されます。

:::warning
署名シークレットはこの画面でしか確認できません。この画面を閉じる前に必ずコピーして安全な場所に保管してください。
紛失した場合は、「署名シークレットをローテーション」で再発行できます。
:::

### テスト送信で動作確認する {#send-test-message}

受信サーバーが起動できたら、ワークフローを実行する前に Webhook が正しく届くかを確認しましょう。
[設定 > Webhook エンドポイント](https://cloud.shisho.dev/*/settings/webhooks) でエンドポイント行の `...` メニューから **「テストメッセージを送信」** を選択すると、テスト用のペイロードが送信されます。

サーバー側でリクエストを受信し、HTTP 2xx を返せていれば準備完了です。

## 署名シークレットをローテーションする {#rotate-secret}

シークレットを再生成したい場合は、エンドポイント行の `...` メニューから **「署名シークレットをローテーション」** を選択します。

ローテーションのあと 24 時間は、本サービスは Webhook 通知時に新旧両方のシークレットで署名し、複数の署名を [Standard Webhooks 仕様](https://www.standardwebhooks.com/) に従ってリクエストに含めます。すなわち、24 時間は「古いシークレットで署名検証をする Webhook 受信サーバー」「新しいシークレットで署名検証をする Webhook 受信サーバー」の両方が、本サービスからの正当な Webhook 通知を受理します。従って、受信サーバーの設定を新しいシークレットに更新する作業を 24 時間以内に行えば、Webhook の受信を一切中断せずにシークレットを更新できます。

手順の例を以下に示します。

1. ローテーションを実行し、新しい署名シークレットを取得する
2. 受信サーバーの環境変数・設定ファイルを新しいシークレットに書き換え、サーバーを再起動する
3. [テスト送信](#send-test-message)で動作を確認する
4. (24 時間経過以降、本サービスからの Webhook 通知には新しいシークレットでの署名のみが含まれる)
