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

# Webhook

> エンドポイントへのリアルタイムなイベント配信 — HMAC 署名付き、リトライあり、セッションごとに順序保証。

Webhook を使うと、あなたのシステムが面接のライフサイクルイベントにリアルタイムで反応できるようになります。最もよく購読されるイベントは `session.completed` ですが、セッションの作成からスコアカードの確定まで、あらゆるイベントを購読できます。

## エンドポイントの登録

<Steps>
  <Step title="開発者設定を開く">
    ワークスペース内の **Settings → Developer → Webhooks** を開きます。
  </Step>

  <Step title="エンドポイントを追加する">
    URL を貼り付け、配信したいイベントを選び、保存します。
  </Step>

  <Step title="署名シークレットをコピーする">
    エンドポイントごとに 32 バイトのシークレットが生成されます。シークレットマネージャーに保管してください — 受信するすべてのリクエストで HMAC 署名を検証するために使用します。
  </Step>
</Steps>

Webhook エンドポイントは、ダッシュボードの **Developer → Webhooks** で管理します — URL を追加し、イベントを選び、署名シークレットをコピーします。

## イベントの種類

| イベント                   | 発火するタイミング                                                 |
| ---------------------- | --------------------------------------------------------- |
| `session.created`      | 新しいセッションの行が作成されたとき（API 呼び出しまたは応募ページからの送信）。                |
| `session.started`      | 候補者が参加し、AI が面接を開始したとき。                                    |
| `session.completed`    | 会話が自然に終了したとき。スコアカードはまだ準備できていない場合があります。                    |
| `session.scored`       | **スコアカードが確定したとき。** 評価項目ごとのブレークダウンが必要な場合は、このイベントを購読してください。 |
| `session.failed`       | 候補者が面接の途中で切断した、または技術的な障害で中断したとき。                          |
| `session.cancelled`    | 候補者が参加する前に採用担当者がキャンセルしたとき。                                |
| `participant.created`  | 新しい参加者が追加されたとき（応募フローまたは API 経由）。                          |
| `application.approved` | 人事が応募を承認したとき — 候補者の面接をスケジュールできます。                         |
| `application.rejected` | 人事が応募を見送ったとき。                                             |

## ペイロードの形式

すべての Webhook は、一貫したエンベロープを持つ JSON ボディを配信します。

```json theme={null}
{
  "id": "evt_a1b2c3d4",
  "event": "session.scored",
  "occurred_at": "2026-06-02T08:21:47Z",
  "data": {
    "session": {
      "id": "e3a1c2d4-...",
      "status": "completed",
      "score": "8.2",
      "passed": true,
      "recommendation": "hire",
      "evaluation_breakdown": [ /* per-dimension */ ],
      "transcript_url": "https://…",
      "recording_url": "https://…",
      "authenticity_signals": { /* … */ },
      "candidate_id": "ba4f2cdd-...",
      "role_id": "ce1cd564-...",
      "external_id": "greenhouse-8821"
    }
  }
}
```

`data` の形式はイベントによって異なります — `participant.*` イベントでは participant が、`application.*` では application が含まれます。

<Tip>
  冪等性: `id` で重複を排除してください。リトライが集中する状況では、同じ論理イベントが複数回配信される場合があります。
</Tip>

## 署名の検証

すべてのリクエストには、エンドポイントのシークレットで署名された、生のボディの HMAC-SHA256 署名を含む `intervyo-signature` ヘッダーが付与されます。

```http theme={null}
intervyo-signature: t=1735689600,v1=2c8ee5d1b5…
```

ペイロードを信頼する前に必ず検証してください — そうしなければ、誰でもあなたの URL にリクエストを送信できてしまいます。

<CodeGroup>
  ```ts TypeScript theme={null}
  import crypto from "crypto";

  function verify(rawBody: string, signatureHeader: string, secret: string) {
    const parts = Object.fromEntries(
      signatureHeader.split(",").map((kv) => kv.split("=") as [string, string]),
    );
    const expected = crypto
      .createHmac("sha256", secret)
      .update(`${parts.t}.${rawBody}`)
      .digest("hex");
    // Constant-time comparison
    return crypto.timingSafeEqual(
      Buffer.from(parts.v1, "hex"),
      Buffer.from(expected, "hex"),
    );
  }
  ```

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

  def verify(raw_body: str, signature_header: str, secret: str) -> bool:
      parts = dict(kv.split("=", 1) for kv in signature_header.split(","))
      expected = hmac.new(
          secret.encode(),
          f"{parts['t']}.{raw_body}".encode(),
          hashlib.sha256,
      ).hexdigest()
      return hmac.compare_digest(parts["v1"], expected)
  ```
</CodeGroup>

<Warning>
  検証は**生のボディ**に対して行ってください — デシリアライズして再シリアライズしてからハッシュ化してはいけません。JSON の空白の変化で署名が一致しなくなります。Express では、パースする前に `express.raw({ type: "application/json" })` で生のボディを取得してください。
</Warning>

## リトライ

エンドポイントが 2xx 以外のステータスを返した場合（または 10 秒でタイムアウトした場合）、プラットフォームは指数バックオフでリトライします。

| 試行 | 待機時間  |
| -- | ----- |
| 1  | 即時    |
| 2  | 30 秒  |
| 3  | 2 分   |
| 4  | 10 分  |
| 5  | 1 時間  |
| 6  | 6 時間  |
| 7  | 24 時間 |

7 回の試行がすべて失敗すると、そのイベントは破棄され、ワークスペース上の `webhook.delivery_failed` メトリクスがインクリメントされます。後続のイベントは引き続き配信が試みられます — 失敗してもエンドポイントが停止されることはありません。

## 順序

イベントは**セッションごとに順序どおり**配信されます。`session.created` が `session.started` より前に、`session.scored` が `session.completed` より後に届くことが保証されます。セッションをまたいだグローバルな順序保証はありません — セッション A のイベントとセッション B のイベントが入り混じって届くことがあります。

## エンドポイントのテスト

Webhook 設定ページには、合成データを使った `session.scored` ペイロードを送信する **Send test event** ボタンがあります。本番のトラフィックが届く前に、エンドポイントと署名検証を確認するのに便利です。

また、エンドポイント詳細ページの **Logs** タブから、過去の任意のイベントを再配信することもできます — バグを修正したあとに、取りこぼしたイベントを補填したいときに役立ちます。
