Ringhum.

개발자

웹훅

통화 종료, 메시지 접수, 예약·주문·숙박 예약 변경, 캠페인 종료 시 서명된 HTTPS 알림을 받고, 각 알림이 실제로 Ringhum에서 온 것인지 확인하는 방법을 안내합니다.

읽는 데 4분 24 9월 2026 업데이트됨

이 페이지의 내용

웹훅은 워크스페이스에서 무언가 발생하는 순간 서버로 HTTPS 요청을 보냅니다. API를 폴링하는 대신 웹훅을 사용하세요. 통화가 끝나면 요약, 결과, 수집된 세부 정보, 통화 기록을 즉시 받을 수 있습니다. 각 전송은 회원님과 Ringhum만 아는 비밀 키로 서명되므로, 서버는 Ringhum에서 오지 않은 요청을 거부할 수 있습니다.

엔드포인트 추가하기

워크스페이스의 소유자관리자만 엔드포인트를 추가, 테스트, 제거할 수 있습니다.

  1. 사이드바에서 개발자를 엽니다.
  2. 웹훅 카드에서 엔드포인트 추가를 클릭합니다.
  3. 엔드포인트 URL을 입력합니다. https://로 시작해야 합니다.
  4. 이벤트에서 원하는 이벤트를 선택합니다. call.ended는 기본으로 선택되어 있습니다.
  5. 엔드포인트 추가를 클릭합니다.
  6. 녹색 상자에서 서명 비밀 키(whsec_로 시작)를 복사하여 서버에 보관합니다. 완료를 클릭합니다.

중요: 서명 비밀 키는 한 번만 표시됩니다. 분실한 경우 엔드포인트를 제거한 후 다시 추가하여 새 키를 받으세요.

필요한 만큼 엔드포인트를 추가할 수 있습니다. 웹훅은 워크스페이스의 모든 어시스턴트를 포함합니다. 쓰기 권한이 있는 키로 POST /webhooks를 사용해 API를 통해서도 엔드포인트를 관리할 수 있습니다.

이벤트

이벤트 전송 시점
call.started 어시스턴트가 통화에 응답함
call.ended 통화가 종료되고 요약됨(통화 기록과 요약 포함)
call.transferred 통화가 사람에게 전달됨
message.taken 어시스턴트가 메시지를 받음
appointment.booked 어시스턴트가 예약을 등록함
appointment.rescheduled 예약 시간이 변경됨
appointment.cancelled 예약이 취소됨
order.created 주문이 접수됨(어시스턴트, 수동, API 중 하나로)
order.updated 주문 상태가 변경됨
order.cancelled 주문이 취소됨
reservation.created 객실이 예약됨(어시스턴트, 수동, API 중 하나로)
reservation.updated 예약 상태가 변경됨(확정, 체크인, 체크아웃, 노쇼)
reservation.cancelled 예약이 취소됨
campaign.completed 발신 캠페인이 목록 전체에 대한 통화를 완료함
test.ping 테스트 전송을 클릭함

전송 내용의 형태

모든 전송은 JSON 본문과 다음 헤더가 포함된 POST 요청입니다:

POST https://example.com/hooks/ringhum
Content-Type: application/json
User-Agent: Ringhum-Webhooks/1.0
X-Ringhum-Event: call.ended
X-Ringhum-Timestamp: 1789250000
X-Ringhum-Signature: v1=5f1a…

{
  "id": "evt_8f2k…",
  "type": "call.ended",
  "created_at": "2026-09-24T14:03:11+00:00",
  "data": {
    "call_id": 4812,
    "direction": "inbound",
    "from": "+14155550132",
    "to": "+14155550100",
    "assistant_id": 3,
    "status": "completed",
    "outcome": "message_taken",
    "sentiment": "neutral",
    "duration_seconds": 94,
    "summary": "Caller asked for a quote for a kitchen repair…",
    "extracted": { "name": "Dana Lee", "reason": "Quote" },
    "transcript": [
      { "role": "assistant", "content": "Hello, you've reached…" },
      { "role": "user", "content": "Hi, I'd like a quote…" }
    ]
  }
}

data 객체는 이벤트에 따라 달라집니다:

  • call.endedmessage.taken: 위의 통화 관련 필드
  • call.startedcall.transferred: call_id, direction, from, to, assistant_id, status, started_at
  • appointment.*: appointment_id, title, status, starts_at, ends_at, timezone, customer(이름, 전화번호, 이메일), contact_id, service, staff, call_id, notes
  • order.*: order_id, number, status, fulfillment, customer, address, 옵션과 가격이 포함된 items, currency, subtotal, fee, total, source, call_id, 취소 시 cancel_reason
  • reservation.*: reservation_id, number, status, room, check_in, check_out, nights, adults, children, guest, currency, nightly, total, source
  • campaign.completed: campaign_id, name, 캠페인 진행 상황 수치

참고: 통화 중 어시스턴트가 예약을 등록하면 appointment.booked를 두 번 받을 수 있습니다. 하나는 예약이 등록되는 시점에 예약 관련 필드와 함께 전송되고, 다른 하나는 통화가 요약된 후 통화 관련 필드와 함께 전송됩니다. dataappointment_id가 있는지 call_id가 있는지 확인하고, id 필드를 사용해 중복 처리를 방지하세요.

서명 확인하기

서명은 타임스탬프, 마침표, 원본 요청 본문을 엔드포인트의 서명 비밀 키로 키잉한 HMAC-SHA256입니다. v1= 뒤에 16진수 다이제스트가 이어지는 형태로 전송됩니다.

  1. JSON 파싱 전, 수신한 그대로의 원본 본문을 읽습니다.
  2. {X-Ringhum-Timestamp}.{원본 본문} 문자열을 만듭니다.
  3. 비밀 키로 HMAC-SHA256을 계산하고 앞에 v1=을 붙입니다.
  4. 상수 시간 비교로 X-Ringhum-Signature와 비교합니다.
  5. 재전송 공격을 막기 위해 타임스탬프가 5분 이상 지난 요청은 거부합니다.

Node.js:

const crypto = require('crypto');

function isValid(req, rawBody, secret) {
  const ts = req.header('X-Ringhum-Timestamp');
  const sig = req.header('X-Ringhum-Signature') || '';
  const expected = 'v1=' + crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

PHP:

$ts = $_SERVER['HTTP_X_RINGHUM_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_RINGHUM_SIGNATURE'] ?? '';
$expected = 'v1=' . hash_hmac('sha256', $ts . '.' . file_get_contents('php://input'), $secret);
if (! hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
    http_response_code(400);
    exit;
}

응답, 타임아웃, 재시도

  • 전송을 확인하려면 2xx 상태로 응답하세요. 시간이 걸리는 작업은 응답 후에 처리하세요.
  • Ringhum은 응답을 최대 10초까지 기다립니다.
  • 엔드포인트가 2xx로 응답하지 않으면, 전송은 총 3회까지 재시도됩니다: 즉시, 약 10초 후, 마지막으로 약 60초 후. 이후에는 폐기됩니다.
  • 전송은 순서가 뒤바뀌어 도착할 수 있습니다. created_atdata의 ID를 사용해 순서를 정렬하세요.

웹훅 표에는 각 엔드포인트의 마지막 HTTP 상태와 전송 시각이 표시됩니다. 실패한 엔드포인트도 계속 활성 상태로 유지되며, Ringhum이 자동으로 꺼주지는 않습니다.

테스트, 일시 중지, 제거

  • 테스트 전송test.ping 이벤트를 즉시 보내며, 엔드포인트가 반환한 HTTP 상태가 메시지로 표시됩니다.
  • 활성 스위치로 엔드포인트를 일시 중지할 수 있습니다. 꺼져 있는 동안 발생한 이벤트는 나중에 전송되지 않습니다.
  • 제거는 엔드포인트와 비밀 키를 삭제합니다.

자주 묻는 질문

연동 페이지의 Zapier, Make, n8n 앱과 무엇이 다른가요? 그 연결들은 연동 페이지와 6가지 이벤트 유형을 사용합니다. 본문은 {"event", "workspace", "sent_at", "data"} 형태이며, 시크릿을 설정하면 서명 헤더는 본문만을 HMAC-SHA256으로 처리한 X-Ringhum-Signature: sha256=…입니다. 여기서 설명하는 개발자용 웹훅은 더 많은 이벤트를 다루며 타임스탬프가 포함된 v1= 서명을 사용합니다. 전송할 항목 선택하기를 참고하세요.

엔드포인트가 2xx를 반환하는데 코드에서는 아무것도 보이지 않습니다. 해당 엔드포인트에서 이벤트가 선택되어 있는지, 엔드포인트가 활성 상태인지, 방화벽이 인터넷에서의 요청을 허용하는지 확인하세요.

서명 확인이 실패하는 이유는 무엇인가요? 가장 흔한 원인은 해싱 전에 본문을 파싱하고 다시 인코딩한 경우입니다. 수신한 그대로의 원본 바이트를 해싱하세요.

여전히 해결되지 않았나요?

[email protected]로 이메일을 보내거나 메시지를 보내주세요. Team 및 Scale 요금제는 우선 지원을 받습니다.

지원팀 문의