Webhook を使うと、あなたのシステムが面接のライフサイクルイベントにリアルタイムで反応できるようになります。最もよく購読されるイベントは session.completed ですが、セッションの作成からスコアカードの確定まで、あらゆるイベントを購読できます。
エンドポイントの登録
開発者設定を開く
ワークスペース内の Settings → Developer → Webhooks を開きます。
エンドポイントを追加する
URL を貼り付け、配信したいイベントを選び、保存します。
署名シークレットをコピーする
エンドポイントごとに 32 バイトのシークレットが生成されます。シークレットマネージャーに保管してください — 受信するすべてのリクエストで HMAC 署名を検証するために使用します。
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 ボディを配信します。
{
"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 が含まれます。
冪等性: id で重複を排除してください。リトライが集中する状況では、同じ論理イベントが複数回配信される場合があります。
署名の検証
すべてのリクエストには、エンドポイントのシークレットで署名された、生のボディの HMAC-SHA256 署名を含む intervyo-signature ヘッダーが付与されます。
intervyo-signature: t=1735689600,v1=2c8ee5d1b5…
ペイロードを信頼する前に必ず検証してください — そうしなければ、誰でもあなたの URL にリクエストを送信できてしまいます。
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"),
);
}
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)
検証は生のボディに対して行ってください — デシリアライズして再シリアライズしてからハッシュ化してはいけません。JSON の空白の変化で署名が一致しなくなります。Express では、パースする前に express.raw({ type: "application/json" }) で生のボディを取得してください。
リトライ
エンドポイントが 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 タブから、過去の任意のイベントを再配信することもできます — バグを修正したあとに、取りこぼしたイベントを補填したいときに役立ちます。