콘텐츠로 이동

웹훅

웹훅을 사용하면 번호표 참여, 호출, 완료 등 대기열 이벤트가 발생할 때 여러분의 시스템이 실시간 HTTP POST 알림을 받을 수 있습니다.

  1. admin.jonot.io/settings/integrations를 엽니다.
  2. 엔드포인트 추가를 클릭합니다.
  3. 서버가 수신 대기 중인 공개 HTTPS URL을 입력합니다.
  4. 구독할 이벤트를 선택합니다.
  5. 저장을 클릭합니다.

서명 시크릿을 저장해 두세요 — 한 번만 표시됩니다. 엔드포인트가 도달 가능한지 확인하려면 엔드포인트 편집 페이지의 테스트 요청 보내기 버튼을 사용하세요.

이벤트 이름발생 시점
ticket.joined고객이 대기열에 참여할 때
ticket.called직원이 번호표를 창구로 호출할 때
ticket.completed번호표가 완료로 표시될 때
ticket.cancelled고객 또는 직원이 번호표를 취소할 때
ticket.skipped번호표가 건너뛰어질 때(재호출 가능한 유예)
ticket.no_show호출되었거나 건너뛴 번호표가 부재로 확정될 때
queue.status_changed대기열 상태가 변경될 때(ACTIVE, PAUSED, 또는 CLOSED)

모든 전송은 다음을 포함하는 HTTP POST입니다:

Content-Type: application/json
X-Jonot-Event: ticket.called
X-Jonot-Delivery-Id: <uuid>
X-Jonot-Timestamp: 2024-06-01T12:00:00.000Z
X-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건
  1. 엔드포인트 편집 페이지를 엽니다.
  2. 서명 시크릿 교체를 클릭합니다.
  3. 대화 상자에서 교체를 확인합니다.
  4. 새 시크릿을 즉시 복사하세요 — 한 번만 표시되며 다시 확인할 수 없습니다. 저장하지 않고 대화 상자를 닫으면 새 평문을 얻기 위해 다시 교체해야 합니다.
  5. 새 시크릿으로 서명을 검증하도록 서버를 업데이트합니다.

중첩 유효 기간(overlap window)은 없습니다. 교체를 확인하는 즉시 기존 시크릿은 검증 기능을 잃습니다. 교체 직후 새 시크릿을 수신 서버에 바로 배포할 수 있도록 계획하세요.

UI에는 Rotate 버튼 옆에 현재 적용 중인 시크릿을 확인할 수 있도록 Active secret: ····XXXX(마지막 네 자리)가 표시됩니다 — 교체 후 수신 서버와 서버가 동기화되었는지 확인하는 데 유용합니다.

교체해야 할 때:

  • 유출 의심: 시크릿이 로그 파일에 남았거나, 퇴사하는 직원과 공유되었거나, 화면 녹화에 캡처된 경우.
  • 정기적인 습관: 주기적으로 교체하면 미탐지 노출로 인한 피해 범위를 제한할 수 있습니다.
  • 시크릿을 읽을 수 있는 서비스에 변경이 생긴 후(키 관리 교체).

수신 서버 업데이트 방법:

  1. 관리자 UI에서 교체하고 새 시크릿을 복사합니다.
  2. 수신 서버의 시크릿 저장소(환경 변수, 시크릿 관리자 등)에서 시크릿을 업데이트합니다.
  3. 업데이트된 수신 서버를 배포합니다.
  4. 전송 내역 탭에서 다음 전송이 성공하는지 확인합니다.

새 시크릿을 저장하기 전에 잃어버렸다면:

다시 교체하세요. 교체할 때마다 새로운 무작위 시크릿이 생성됩니다. 이전 평문은 복구할 수 없으며 — 서버 측에는 AEAD 암호화된 형태만 저장됩니다.