OdyOdy 도움말

웹훅 수신 및 서명 확인

업데이트됨: Sun Aug 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

Ody는 새 메시지, 통화 결과, 연락처 변경과 같은 작업 공간 이벤트를 API를 통해 등록하는 HTTPS 엔드포인트로 푸시하며, 모든 전달은 표준 웹훅 사양에 따라 서명되므로 Ody에서 전송되었음을 증명할 수 있습니다.

시작하기 전에

  • webhooks:manage 범위가 있는 API 키가 필요합니다. — Ody API 키 가져오기를 참조하세요.
  • 엔드포인트는 2xx 상태로 빠르게 응답하는 HTTPS URL이어야 합니다 (전달은 10초 후에 시간 초과됩니다).
  • 각 작업 공간은 최대 50개의 엔드포인트를 등록할 수 있습니다.

단계별 안내

  1. 구독할 수 있는 이벤트 유형을 나열합니다:
curl https://api.ody.co/v1/api/webhook-events \
  -H "Authorization: Bearer ody_live_…"

현재 카탈로그: message.received, message.delivered, message.failed, call.ringing, call.answered, call.completed, call.forwarded, call.missed, call.recording.completed, call.summary.completed, call.transcript.completed, call.voicemail.completed, call.flow.started, call.flow.completed, call.flow.aborted, contact.updated, contact.deleted. "*"를 구독하면 현재 및 향후의 모든 이벤트 유형을 포함합니다.

call.flow.* 이벤트는 통화 흐름에 의해 통화가 응답될 때 발생합니다: 발신자가 흐름에 진입할 때 started, 목적지(착신, AI 비서, 음성 사서함, 전달 또는 끊기)에 도달할 때 completed, 흐름을 완료할 수 없을 때 aborted가 발생합니다.

  1. 엔드포인트를 등록합니다:
curl https://api.ody.co/v1/api/webhooks \
  -H "Authorization: Bearer ody_live_…" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/ody-webhook","events":["message.received"],"label":"prod"}'
  1. 응답에서 secret을 즉시 저장하세요. whsec_… 서명 비밀 키는 생성 및 회전 시에만 반환되며, 다시는 표시되지 않습니다.
  2. 서명된 테스트 이벤트를 자신에게 보내고 엔드포인트가 이를 확인하고 수락하는지 확인합니다:
curl -X POST https://api.ody.co/v1/api/webhooks/WEBHOOK_ID/test \
  -H "Authorization: Bearer ody_live_…"

테스트는 실제 전달과 동일하게 서명된 {"type":"ping"} 이벤트를 게시합니다.

전달의 모습

모든 전달은 JSON 본문과 세 개의 서명 헤더를 포함하는 POST입니다:

{
  "id": "evt_…",
  "apiVersion": "2026-06-01",
  "createdAt": "2026-08-16T12:00:00.000Z",
  "type": "message.received",
  "data": { "conversationId": "…", "activityId": "…", "numberE164": "+1…" }
}
헤더 의미
webhook-id 이벤트 ID — 재시도 시 재사용되므로 이를 기준으로 중복 제거
webhook-timestamp 전송 시점의 Unix 초
webhook-signature <id>.<timestamp>.<raw body>에 대한 v1,<base64 HMAC-SHA256>

서명 확인

HMAC 키는 whsec_ 접두사 뒤의 비밀 키의 base64-디코딩된 부분이며, 서명된 메시지는 <webhook-id>.<webhook-timestamp>.<raw body>입니다. 항상 원시 요청 바이트에 대해 확인해야 합니다. — 파싱된 JSON을 다시 직렬화하면 서명이 손상됩니다. 몇 분 이상 오래된 타임스탬프는 재생 공격을 차단하기 위해 거부하세요.

import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();
app.post("/ody-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const secret = process.env.ODY_WEBHOOK_SECRET; // whsec_…
  const id = req.headers["webhook-id"];
  const timestamp = req.headers["webhook-timestamp"];
  const signature = req.headers["webhook-signature"];

  // Replay guard: reject deliveries older than 5 minutes.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();

  const key = Buffer.from(secret.slice("whsec_".length), "base64");
  const expected = "v1," + createHmac("sha256", key)
    .update(`${id}.${timestamp}.${req.body}`)
    .digest("base64");

  const a = Buffer.from(expected);
  const b = Buffer.from(String(signature));
  if (a.length !== b.length || !timingSafeEqual(a, b)) return res.status(401).end();

  const event = JSON.parse(req.body.toString("utf8"));
  console.log("verified event:", event.type);
  res.status(200).end();
});

공식 SDK는 이를 한 줄로 제공합니다. — verifyWebhookSignature (TypeScript) 및 verify_webhook_signature (Python)는 모두 상수 시간 비교와 5분 재생 방지 기능이 내장되어 있습니다. SDK 빠른 시작: TypeScript 및 Python을 참조하세요.

재시도, 기록 및 관리

  • 재시도: 실패한 전달(2xx가 아닌 응답, 시간 초과 또는 연결 오류)은 첫 번째 시도 후 약 5초 그리고 30초 후에 재시도됩니다. — 총 세 번의 시도, 각 시도당 10초 시간 초과.
  • 기록: GET /v1/api/webhooks/:id/deliveries는 최신 순으로 최근 전달 목록을 표시하며, 시도별 상태 코드, 오류 및 지속 시간을 포함합니다. — 이벤트가 누락된 것처럼 보일 때 가장 먼저 확인할 곳입니다.
  • 엔드포인트 관리: GET /v1/api/webhooks는 엔드포인트를 나열하고(비밀 키는 표시되지 않음), PATCH /v1/api/webhooks/:idurl, events, label 또는 status(enabled/disabled)를 업데이트하며, DELETE /v1/api/webhooks/:id는 전달 기록과 함께 엔드포인트를 제거합니다.
  • POST /v1/api/webhooks/:id/rotate를 사용하여 언제든지 비밀 키를 회전할 수 있습니다. — 새 whsec_…는 한 번만 반환되며, 이전 서명은 즉시 유효하지 않게 됩니다.

문제 해결

  • 서명 확인이 항상 실패합니다 — 아마도 다시 직렬화된 본문을 확인하고 있을 것입니다. 원시 요청 바이트를 사용하고, whsec_를 제거한 후 비밀 키를 base64-디코딩했는지 확인하세요.
  • 전달이 도착하지 않습니다 — 엔드포인트의 statusenabled인지, events 목록에 예상하는 유형(또는 "*"), 그리고 시도별 오류에 대한 전달 기록을 확인하세요.
  • 중복 이벤트 — 재시도는 동일한 webhook-id를 재사용합니다. 이를 기준으로 중복을 제거하세요.
  • webhook limit reached — 작업 공간에 대해 50개의 엔드포인트에 도달했습니다. 먼저 사용하지 않는 엔드포인트를 삭제하세요.

관련 문서

자주 묻는 질문

웹훅 전달은 어떻게 서명되나요?

표준 웹훅 사양에 따라: whsec_ 비밀 키를 사용하여 '<webhook-id>.<webhook-timestamp>.<raw body>'에 대해 계산된 v1,<base64 HMAC-SHA256> 형식의 webhook-signature 헤더입니다.

엔드포인트가 다운되면 어떻게 되나요?

Ody는 첫 번째 실패 후 약 5초, 그리고 30초 후에 두 번 재시도합니다. 시도별 상태 코드 및 오류는 GET /v1/api/webhooks/:id/deliveries에서 확인하세요.

서명 비밀 키를 잃어버렸습니다. 어떻게 해야 하나요?

POST /v1/api/webhooks/:id/rotate를 사용하여 회전시키세요. 새 whsec_ 비밀 키는 한 번만 반환되며, 이전 서명은 즉시 유효하지 않게 됩니다.

개발자 API에서 더 보기