웹훅
웹훅을 사용하면 번호표 참여, 호출, 완료 등 대기열 이벤트가 발생할 때 여러분의 시스템이 실시간 HTTP POST 알림을 받을 수 있습니다.
시작하기
섹션 제목: “시작하기”- admin.jonot.io/settings/integrations를 엽니다.
- 엔드포인트 추가를 클릭합니다.
- 서버가 수신 대기 중인 공개 HTTPS URL을 입력합니다.
- 구독할 이벤트를 선택합니다.
- 저장을 클릭합니다.
서명 시크릿을 저장해 두세요 — 한 번만 표시됩니다. 엔드포인트가 도달 가능한지 확인하려면 엔드포인트 편집 페이지의 테스트 요청 보내기 버튼을 사용하세요.
이벤트 유형
섹션 제목: “이벤트 유형”| 이벤트 이름 | 발생 시점 |
|---|---|
ticket.joined | 고객이 대기열에 참여할 때 |
ticket.called | 직원이 번호표를 창구로 호출할 때 |
ticket.completed | 번호표가 완료로 표시될 때 |
ticket.cancelled | 고객 또는 직원이 번호표를 취소할 때 |
ticket.skipped | 번호표가 건너뛰어질 때(재호출 가능한 유예) |
ticket.no_show | 호출되었거나 건너뛴 번호표가 부재로 확정될 때 |
queue.status_changed | 대기열 상태가 변경될 때(ACTIVE, PAUSED, 또는 CLOSED) |
페이로드 형식
섹션 제목: “페이로드 형식”모든 전송은 다음을 포함하는 HTTP POST입니다:
Content-Type: application/jsonX-Jonot-Event: ticket.calledX-Jonot-Delivery-Id: <uuid>X-Jonot-Timestamp: 2024-06-01T12:00:00.000ZX-Jonot-Signature: v1,<base64-hmac-sha256>X-Jonot-Payload-Version: v1기본 본문은 JSON 봉투(envelope)입니다. payload 필드의 형태는 이벤트에
따라 달라집니다:
ticket.joined
{ "deliveryId": "<uuid>", "event": "ticket.joined", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "queueId": "q_…" }, "queue": { "id": "q_…", "name": "Main Queue" }, "location": { "id": "loc_…", "name": "Downtown" } }}ticket.called / ticket.completed / ticket.skipped / ticket.no_show
{ "deliveryId": "<uuid>", "event": "ticket.called", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "number": 42, "status": "CALLED", "queueId": "q_…", "createdAt": "2024-06-01T11:58:00.000Z", "updatedAt": "2024-06-01T12:00:00.000Z" } }}ticket.cancelled
{ "deliveryId": "<uuid>", "event": "ticket.cancelled", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticketId": "tkt_…", "queueId": "q_…" }}queue.status_changed
{ "deliveryId": "<uuid>", "event": "queue.status_changed", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "queueId": "q_…", "status": "PAUSED" }}서명 검증하기
섹션 제목: “서명 검증하기”Jonot은 다음 문자열에 대해 HMAC-SHA256으로 모든 전송에 서명합니다:
<deliveryId>.<timestamp>.<body>Node.js(18 이상)에서 검증하려면:
import { createHmac, timingSafeEqual } from "node:crypto";
/** * Returns true when the signature header is valid and the timestamp is * within 5 minutes of now. Throws for malformed input. */function verifySignature(secret, deliveryId, timestamp, body, header) { // Replay-attack guard: reject deliveries older than 5 minutes. const ageMs = Date.now() - new Date(timestamp).getTime(); if (Math.abs(ageMs) > 5 * 60 * 1000) return false;
const expected = "v1," + createHmac("sha256", secret) .update(`${deliveryId}.${timestamp}.${body}`) .digest("base64");
// timingSafeEqual prevents timing-oracle attacks. // Buffers must be the same length — if lengths differ the signature is // invalid, but we still compare a dummy value to keep constant time. const expectedBuf = Buffer.from(expected); const headerBuf = Buffer.from(header); if (expectedBuf.length !== headerBuf.length) return false; return timingSafeEqual(expectedBuf, headerBuf);}페이로드 템플릿
섹션 제목: “페이로드 템플릿”기본 JSON 본문을 사용자 지정 템플릿으로 대체할 수 있습니다. 템플릿은 Mustache-lite 문법을 사용합니다:
{{ path.to.value }}— 콘텐츠 유형에 맞게 치환 및 이스케이프됩니다:application/json은 JSON 문자열 이스케이프,application/x-www-form-urlencoded는 퍼센트 인코딩,text/plain은 그대로 사용됩니다{{{ path.to.value }}}— 이스케이프 없이 그대로 치환됩니다
application/json용 템플릿 예시(ticket.joined 구독 시):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}템플릿 편집기
섹션 제목: “템플릿 편집기”페이로드 템플릿 필드는 다음 기능을 갖춘 완전한 코드 편집기입니다:
- JSON 템플릿을 위한 구문 강조와 괄호 매칭.
- 변수 팔레트 — 선택한 이벤트에서 사용 가능한 각 변수 경로에 대한 삽입
버튼이 항상 표시되는 행입니다. 버튼을 클릭하면 커서 위치에
{{ path }}토큰이 삽입됩니다. - 실시간 검증 — 입력하는 동안 편집기는 JSON 구조(콘텐츠 유형이
application/json이고 raw{{{ }}}태그가 없는 경우)와 변수 경로를 모두 검사합니다. 진단 결과는 편집기 내 인라인 표시와 아래 요약 배너로 나타납니다:- 오류 — 선택한 모든 이벤트에서 알 수 없는 경로.
- 경고 — 선택한 이벤트 중 일부에만 존재하는 경로(다른 이벤트의 전송 에서는 값이 비어 있게 됩니다).
검증은 저장 시점에도 서버에서 실행되며, 편집기의 피드백은 서버 규칙을 그대로 반영합니다.
전송 재시도
섹션 제목: “전송 재시도”전송 실패(2xx가 아닌 응답 또는 연결 오류)는 Cloudflare Queues가 지수 백오프로 최대 3회까지 재시도합니다. 3회 실패 후 전송은 데드레터 큐로 이동하며 엔드포인트의 연속 실패 카운터가 증가합니다.
엔드포인트가 연속 20회 실패를 누적하면 자동으로 비활성화됩니다. 엔드포인트 편집 페이지에서 다시 활성화할 수 있으며, 카운터는 0으로 재설정됩니다.
전송 기록
섹션 제목: “전송 기록”각 엔드포인트의 전송 내역 탭에는 최근 30일간의 전송 시도가 표시됩니다: 이벤트 유형, HTTP 상태, 시도 횟수, 타임스탬프. 더 보기 버튼을 사용해 이전 기록을 더 불러올 수 있습니다.
제한 사항
섹션 제목: “제한 사항”| 제한 | 값 |
|---|---|
| 조직당 엔드포인트 수 | 5 |
| 엔드포인트당 사용자 지정 헤더 | 10 |
| 헤더 값 길이 | 1,024바이트 |
| 페이로드 템플릿 크기 | 16 KB |
| 전송 제한 시간 | 10초 |
| 전송 기록 보존 기간 | 30일 |
| 조직당 최대 전송 속도 | 60초당 120건 |
서명 시크릿 교체하기
섹션 제목: “서명 시크릿 교체하기”- 엔드포인트 편집 페이지를 엽니다.
- 서명 시크릿 교체를 클릭합니다.
- 대화 상자에서 교체를 확인합니다.
- 새 시크릿을 즉시 복사하세요 — 한 번만 표시되며 다시 확인할 수 없습니다. 저장하지 않고 대화 상자를 닫으면 새 평문을 얻기 위해 다시 교체해야 합니다.
- 새 시크릿으로 서명을 검증하도록 서버를 업데이트합니다.
중첩 유효 기간(overlap window)은 없습니다. 교체를 확인하는 즉시 기존 시크릿은 검증 기능을 잃습니다. 교체 직후 새 시크릿을 수신 서버에 바로 배포할 수 있도록 계획하세요.
UI에는 Rotate 버튼 옆에 현재 적용 중인 시크릿을 확인할 수 있도록
Active secret: ····XXXX(마지막 네 자리)가 표시됩니다 — 교체 후 수신
서버와 서버가 동기화되었는지 확인하는 데 유용합니다.
서명 시크릿 관리 습관
섹션 제목: “서명 시크릿 관리 습관”교체해야 할 때:
- 유출 의심: 시크릿이 로그 파일에 남았거나, 퇴사하는 직원과 공유되었거나, 화면 녹화에 캡처된 경우.
- 정기적인 습관: 주기적으로 교체하면 미탐지 노출로 인한 피해 범위를 제한할 수 있습니다.
- 시크릿을 읽을 수 있는 서비스에 변경이 생긴 후(키 관리 교체).
수신 서버 업데이트 방법:
- 관리자 UI에서 교체하고 새 시크릿을 복사합니다.
- 수신 서버의 시크릿 저장소(환경 변수, 시크릿 관리자 등)에서 시크릿을 업데이트합니다.
- 업데이트된 수신 서버를 배포합니다.
- 전송 내역 탭에서 다음 전송이 성공하는지 확인합니다.
새 시크릿을 저장하기 전에 잃어버렸다면:
다시 교체하세요. 교체할 때마다 새로운 무작위 시크릿이 생성됩니다. 이전 평문은 복구할 수 없으며 — 서버 측에는 AEAD 암호화된 형태만 저장됩니다.