Webhooks — イベント、 署名、 リプレイ防止
Webhook は
api_versionで webhook 単位に契約を出し分ける。 既存 (v1) は v1 形式 body +X-Webhook-*、 新規は v2 契約で配信される。
契約バージョン (api_version) による出し分け
api_version | HTTP body | 署名ヘッダー | User-Agent |
|---|---|---|---|
2025-10-23 (v1, 既存) | v1 形式 {event, timestamp, roomId, data, deliveryId} | X-Webhook-Signature (sunset まで) + Tascha-Webhook-* (移行用に並送) | TasCha-Webhook/1.0 |
2026-06-09 (v2) | v2 形式 (下記) | Tascha-Webhook-* のみ | TasCha-Webhook/2.0 |
- 既存の webhook は v1 契約のまま配信され、 receiver の挙動は変わらない。 新規登録は v2 契約。
- v1 行にも
Tascha-Webhook-Signature(raw body 方式) が並送されるため、 receiver は body 形式を変えずに署名検証だけ先に v2 へ移行 できる。 その後api_versionを進めて body を v2 化する 2 段階移行が可能。 - v1 → v2 の昇格は webhook の再作成で行う (自動昇格・部分更新は非対応)。
設計思想
- Payload は ID-only にする。 詳細データは API で取得する (Notion 同様)
- HMAC-SHA256 を raw body bytes に対して計算 (
JSON.stringifyではない) - signature には
timestampを含め、 5 分の replay window - secret は rotation:
currentとnextの 2 本 - delivery 失敗時の retry / redelivery API を提供
Payload 形式 (Webhook v2)
{
"id": "evt_NTcwY2NjZGM2N2QxZjFiZA",
"type": "record.created",
"apiVersion": "2026-06-09",
"createdAt": "2026-06-09T12:00:00.000Z",
"organizationId": "org_...",
"roomId": "room_...",
"data": { "object": { "id": "rec_...", "type": "record" } }
}
id は evt_ prefix + 16 byte 暗号乱数を URL-safe base64 (= 22 文字) でエンコードしたもの。 sortable ではない ため受信側で時系列ソートに使わない (createdAt を使う)。 過去ログとの突合は文字列等価で十分。
data.object は event 種別ごとに polymorphic に変わる:
| event | data.object.type | data.object.id |
|---|---|---|
record.* / approval.* | record | 議事録 ID (rec_...) |
message.created | message | メッセージ ID |
task.* | task | タスク ID |
file.uploaded | file | ファイル ID |
room.created / room.member.added | room | room ID |
詳細データが必要なら GET /v1/rooms/{roomId}/records/{recordId} 等で取りに行く (Public API)。
Headers
Tascha-Webhook-Id: evt_...
Tascha-Webhook-Delivery: <uuid>
Tascha-Webhook-Timestamp: 1780987200
Tascha-Webhook-Signature: v1=<hex> # rotation 中は "v1=<hex>,v1=<hex>"
Tascha-Webhook-Type: record.created
Tascha-Webhook-Api-Version: 2026-06-09
User-Agent: TasCha-Webhook/2.0
# v1 契約行のみ (sunset 2026-12-31 まで): 旧 v1 ヘッダーも並行送信される
X-Webhook-Signature: <hex> # HMAC-SHA256 over 実送信 raw body (v1 形式)
X-Webhook-Event: record.created
X-Webhook-Delivery-Id: <uuid>
Sunset: Thu, 31 Dec 2026 00:00:00 GMT # RFC 8594 (deprecation 予告)
Deprecation: true
Tascha-Webhook-Delivery は配信冪等キーで、 同一ジョブの自動リトライ間では同一値、 redeliver では新しい値になる。 Public API の配信履歴では deliveryId フィールドとして返り、 ヘッダーと突合できる (履歴行そのものの ID は id)。
カスタムヘッダーの優先順位
webhook 登録時に指定したカスタムヘッダーは Content-Type / User-Agent の既定値を
上書きできる (v1 からの互換挙動。 application/vnd.api+json 送信や受信側の
UA allowlist 連携を想定)。 一方、 署名・配信メタの契約ヘッダー (Tascha-Webhook-*、
legacy X-Webhook-*、 Sunset / Deprecation) はカスタムヘッダーでは
上書き・偽装できない。