Webhookペイロードリファレンス
KoeIQは分析完了・アラート発火などのイベント発生時に、設定したエンドポイントへHTTP POSTリクエストを送信します。すべてのペイロードはHMAC-SHA256で署名されます。
イベント種別
| イベント | 発火タイミング |
|---|---|
| transcription.completed | 文字起こし処理完了時 |
| analytics.completed | AI分析(品質スコア・感情・意図)完了時 |
| alert.triggered | アラートルールの条件を満たした時 |
transcription.completed
{
"event": "transcription.completed",
"timestamp": "2026-03-17T09:00:00Z",
"tenant_id": "t_xxxx",
"data": {
"voicelog_id": "vl_xxxx",
"call_id": "CALL-20260317-001",
"status": "done",
"duration_seconds": 342,
"language": "ja-JP",
"operator_id": "OP001",
"department": "Support",
"call_date": "2026-03-17"
}
}analytics.completed
{
"event": "analytics.completed",
"timestamp": "2026-03-17T09:01:30Z",
"tenant_id": "t_xxxx",
"data": {
"voicelog_id": "vl_xxxx",
"call_id": "CALL-20260317-001",
"quality_score": 82,
"sentiment_overall": 0.4,
"dominant_emotion": "neutral",
"intent_primary": "billing_inquiry",
"summary": "Customer called about invoice discrepancy..."
}
}alert.triggered
{
"event": "alert.triggered",
"timestamp": "2026-03-17T09:02:00Z",
"tenant_id": "t_xxxx",
"data": {
"alert_rule_id": "ar_xxxx",
"alert_rule_name": "Low Quality Score",
"voicelog_id": "vl_xxxx",
"call_id": "CALL-20260317-001",
"condition": "quality_score_below",
"threshold": 60,
"actual_value": 45,
"operator_id": "OP001"
}
}署名の検証
すべてのリクエストにはX-KoeIQ-Signatureヘッダーが含まれます。値はsha256=<hex>形式で、ペイロードボディをWebhookシークレットでHMAC-SHA256署名したものです。
警告: 必ず生のリクエストボディ(バイト列)で署名を検証してください。パース後のJSONでは署名が一致しません。
Python
import hmac, hashlib
def verify_signature(payload_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature_header)
# In your webhook handler:
sig = request.headers.get("X-KoeIQ-Signature", "")
if not verify_signature(request.body, sig, "your_webhook_secret"):
return 401Node.js
import crypto from "crypto";
function verifySignature(payload: string, signatureHeader: string, secret: string): boolean {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
// In your webhook handler:
const sig = req.headers["x-koeiq-signature"] as string;
if (!verifySignature(rawBody, sig, process.env.WEBHOOK_SECRET!)) {
return res.status(401).json({ error: "Invalid signature" });
}