무엇을 도와드릴까요?

문의 내용을 간단히 알려 주세요. 이메일로 답변드릴게요.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
홈으로

웹훅: 서버로 이벤트 전달

reactive.chat의 서명된 웹훅과 REST 훅: 이벤트, 헤더, 본문, HMAC-SHA256 서명 검증, 재시도, 시간 제한, 비활성화와 PHP·Node·Python 검증 예제.

웹훅은 새 대화, 새 메시지, 팀 인계, 평가 등 채팅에서 일어나는 일을 서버에 알립니다. 두 경로가 있습니다.

이벤트

웹사이트 검색(Smart Answer)의 대화는 채팅으로 표시된 후에만 이벤트를 발생시킵니다.

서명된 웹훅 설정

계정의 설정 → 연동 → 웹훅에서 설정합니다(연동 관리 권한 필요).

저장 시 테스트 호출은 보내지 않습니다. 활성화한 뒤 이벤트가 전달되기까지 약 15초가 걸릴 수 있습니다. 활성화 전 이벤트를 소급 전송하지 않습니다.

전송

POST /rc-webhook HTTP/1.1
Host: hooks.your-company.com
Content-Type: application/json; charset=utf-8
User-Agent: reactive.chat-webhook/1.0
X-RC-Event: csat.submitted
X-RC-Delivery: 01JAB3C4D5E6F7G8H9JKMNPQRS
X-RC-Timestamp: 1792503600
X-RC-Signature: sha256=3f0a1c...

{"id":"01JAB3C4D5E6F7G8H9JKMNPQRS","event":"csat.submitted","ts":1792503600,"site_key":"ABCDEFGHJKLMNPQRSTUVWX12","conversation_id":"01JAB2...","session_id":"s_...","visitor_id":"v_...","data":{"score":5,"comment":"Very helpful"}}

헤더:

본문:

서명 검증

모든 처리 전에 검증합니다. 타임스탬프가 최대 300초 전인지, 원시 본문에 대한 서명인지 확인하고 상수 시간으로 비교합니다.

PHP:

<?php
$geheim = getenv('RC_WEBHOOK_SECRET');
$body   = file_get_contents('php://input');
$ts     = $_SERVER['HTTP_X_RC_TIMESTAMP'] ?? '';
$sig    = $_SERVER['HTTP_X_RC_SIGNATURE'] ?? '';

if (!ctype_digit($ts) || abs(time() - (int) $ts) > 300) { http_response_code(401); exit; }
$soll = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $geheim);
if (!hash_equals($soll, $sig)) { http_response_code(401); exit; }

$ereignis = json_decode($body, true);
// detect duplicates: remember $ereignis['id']
http_response_code(204);

Node.js(Express):

const crypto = require('crypto');
app.post('/rc-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const ts = req.get('X-RC-Timestamp') || '';
  const sig = req.get('X-RC-Signature') || '';
  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
  const soll = 'sha256=' + crypto.createHmac('sha256', process.env.RC_WEBHOOK_SECRET)
    .update(ts + '.' + req.body.toString('utf8')).digest('hex');
  if (sig.length !== soll.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(soll))) {
    return res.sendStatus(401);
  }
  const ereignis = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(204);
  verarbeiteSpaeter(ereignis);   // respond quickly, work afterwards
});

Python(Flask):

import hmac, hashlib, os, time
from flask import request, abort

@app.post("/rc-webhook")
def rc_webhook():
    ts = request.headers.get("X-RC-Timestamp", "")
    sig = request.headers.get("X-RC-Signature", "")
    body = request.get_data()                      # raw bytes
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        abort(401)
    soll = "sha256=" + hmac.new(os.environ["RC_WEBHOOK_SECRET"].encode(),
                                ts.encode() + b"." + body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(soll, sig):
        abort(401)
    return "", 204

응답, 재시도, 비활성화

먼저 빠르게 2xx로 응답하고 이후 처리합니다. 시간 제한은 연결 설정 4초, 전체 10초입니다. 리디렉션은 따르지 않습니다.

1시간보다 오래된 이벤트는 더 이상 전송하지 않습니다. 오류가 누적되면(60초 안에 5회) 대상 전송을 30초 동안 일시 중지하고 이후 작업을 재시도합니다. 채팅은 웹훅을 기다리지 않습니다.

전송 순서는 보장되지 않습니다. 필요하면 ts 또는 API 메시지의 seq로 정렬합니다.

REST 훅(Zapier, Make, n8n)

자동화 도구는 서버 API로 대상을 직접 구독합니다. 직접 사용할 수도 있습니다.

curl -X POST https://reactive.chat/v1/hooks \
  -H "Authorization: Bearer rc_live_..." \
  -H "Content-Type: application/json" \
  -d '{"event":"message.created","target_url":"https://hooks.your-company.com/rc","source":"n8n"}'

REST 훅 전송:

POST https://hooks.your-company.com/rc
Content-Type: application/json
User-Agent: reactive.chat-n8n/1.0
X-RC-Event: message.created
X-RC-Delivery: 01JAB3...

{"event":"message.created","delivery_id":"01JAB3...","data":{"name":"message.created","ts":1792503600123,"site_key":"...","conversation_id":"...","session_id":"...","visitor_id":"...","message":{"id":"...","seq":7,"kind":"text","direction":"in","author_type":"bot","author_name":"Assistant","via":null,"body":"…","redacted":false,"ts":1792503600120}}}

제공되지 않는 기능

다음: 서버 API

최종 업데이트: