Vox Gateway

Webhook

통화·녹음 이벤트를 받도록 설정하고 HMAC 서명을 검증하는 방법.

통화가 시작·종료될 때 gateway가 지정한 주소로 서명된 JSON을 POST합니다. 폴링 없이 결과를 받는 가장 짧은 길입니다.

어떤 이벤트가 있고 페이로드가 어떻게 생겼는지는 API 레퍼런스의 Webhooks에 있습니다 — 이 지면은 그걸 받도록 설정하고 검증하는 방법을 다룹니다.

  • call.ended — 통화가 끝났을 때. 종료 사유·통화 길이·요약이 이때 채워집니다.
  • recording.completed — 녹음 저장이 끝났을 때.
  • 나머지 넷(call.started · call.active · call.silence_detected · recording.failed)도 같은 자리에 있습니다.

Webhook 등록

설정 › 웹훅 한 카드에서 끝납니다 — 아래 넷이 그 카드의 행 순서 그대로입니다.

설정 › 웹훅의 통화 이벤트 전송 카드

활성화를 켜고 수신 URL을 저장

URL은 https여야 합니다. 저장은 카드 아래 [저장] 한 번으로 카드 전체가 함께 커밋됩니다.

받을 이벤트 선택

6종을 개별로 켭니다. 기본은 call.started · call.ended 둘뿐이고 나머지 넷은 꺼져 있습니다 — 기존에 받던 전송이 늘어나지 않게 한 것이라, 새 이벤트는 여기서 켜야 옵니다. 전부 끄면 URL이 있어도 아무것도 발사되지 않습니다.

서명 secret 발급

평문은 발급 시 한 번만 표시되고 이후에는 존재 여부만 보입니다. 여기서 발급한 것이 워크스페이스 공용 secret이고, 번호가 자기 secret을 따로 두지 않았다면 그 번호의 전송도 이 값으로 서명됩니다.

(선택) 번호마다 다르게

번호 상세 › 웹훅의 이 번호만 따로 설정 스위치를 켜면 같은 카드가 그 번호용으로 펼쳐집니다 — 활성화 · URL · 이벤트 · secret 넷을 각각 덮을 수 있습니다. 스위치가 꺼져 있으면 전부 위 공통 설정을 따르고, 켠 뒤에도 비워 둔 항목은 공통값을 그대로 씁니다(예: URL만 바꾸고 secret은 공용 유지).

이 번호에만 전송을 멈추고 싶으면 스위치를 켜고 활성화를 끄면 됩니다 — URL을 비우는 것은 "공통 URL로 보내라"는 뜻이라 전송이 멈추지 않습니다.

워크스페이스 Webhook이 꺼져 있으면 번호에서 활성화를 켜도 전송되지 않습니다 — 위 활성화 토글이 전체 차단 스위치입니다.

서명 검증

secret을 저장해 두면 요청에 서명 헤더 두 개가 함께 옵니다(X-Gateway-Timestamp · X-Gateway-Signature). 서명은 HMAC-SHA256(secret, "{timestamp}.{원문 바디}")의 hex 문자열입니다.

# 서명은 "{timestamp}.{원문 바디}" 를 secret으로 HMAC-SHA256 한 hex 값입니다.
TS="$(printf '%s' "$X_GATEWAY_TIMESTAMP")"
printf '%s.%s' "$TS" "$RAW_BODY" \
  | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex

반드시 수신한 원문 바디로 검증하세요. JSON을 파싱했다가 다시 직렬화하면 공백·유니코드 이스케이프가 달라져 서명이 어긋납니다.

종료 후 연결 시간 확인

call.ended는 종료를 알리는 트리거로 쓰고, 정확한 실제 연결 시간이 필요한 처리는 이벤트의 call.idGet Call을 조회하세요. 응답의 duration_sec는 상대가 받은 시점부터 연결 종료까지의 시간입니다. 웹훅의 call.duration_secondsstarted_at부터 ended_at까지의 시간이라 벨이 울리는 시간도 포함합니다. 두 값을 같은 통화 시간으로 취급하지 마세요.

REST의 duration_secnull이면 진행 중이거나 측정값이 아직 보고되지 않은 상태입니다. 값이 필요하면 이후 다시 조회하고, null을 0초로 처리하지 마세요. 받지 않은 통화 (no_answer)는 0입니다.

녹음 완료 후 파일 받기

recording.completeddata.duration_sec녹음 파일의 길이입니다. 웹훅의 call.duration_seconds, REST 통화 조회의 duration_sec와 구분하세요. 이벤트에는 재생 URL이 담기지 않습니다. 저장소 키인 object_key를 다운로드 URL로 사용하지 마세요.

파일을 직접 수집하려면 이벤트의 call.idGET /api/v1/calls/{call_id}/recording을 호출합니다. 같은 번호 API 키의 Bearer 인증을 사용하며 오디오 바이트를 반환합니다. 녹음 파일 받기에 호출 예시가 있습니다. 브라우저에 공유할 시한부 재생 링크가 필요하면 MCP 녹음 링크 도구를 사용하세요.

전달 규칙

타임아웃·재시도 값은 각 이벤트 지면의 응답 설명에 있습니다. 여기에는 그 값만으로는 안 보이는 것을 적습니다.

  • 전달은 통화 처리를 막지 않습니다 — 여러분의 서버가 죽어도 통화는 정상 진행됩니다. 바꿔 말해 유실될 수 있습니다. 정합성이 중요하면 종료 이벤트를 Get Call로 한 번 더 확인하세요.
  • event_id중복 제거 키로 그대로 쓰세요. 재시도는 처음 만든 본문을 그대로 다시 보내므로 event_id가 같습니다 — 중복이 생기는 유일한 경로에서 값이 안 변한다는 뜻입니다. (타임아웃으로 재시도했는데 사실은 첫 요청이 처리됐던 경우가 여기 해당합니다.)
  • 이벤트 하나당 발사도 한 번입니다 — gateway를 여러 대 굴려도 통화가 시작·종료된 그 인스턴스에서만 나갑니다.

다음 단계

On this page