개요
서명된 HTTP 요청으로 TicketWave 이벤트를 자신의 애플리케이션에서 받아보세요.
웹훅
웹훅은 TicketWave가 당신에게 보내는 HTTP 요청입니다. 서버에서 무언가가 발생할 때마다 — 티켓이 열리거나, 멤버가 블랙리스트에 추가되거나 — TicketWave가 당신이 소유한 URL로 JSON 본문을 POST합니다.
이것이 로그 채널과의 차이입니다. 로그 채널은 사람이 읽을 수 있도록 Discord에 임베드를 작성하고, 웹훅은 원시 이벤트를 당신의 코드에 전달합니다.
웹훅은 프리미엄 기능입니다. 프리미엄이 없으면 엔드포인트를 만들 수 없고 어떤 이벤트도 전달되지 않습니다.
엔드포인트 만들기
엔드포인트 페이지 열기
서버 대시보드 → Webhooks → Endpoints.
엔드포인트 추가하기
Add Endpoint를 클릭하고 두 필드를 입력하세요:
| Field | Description |
|---|---|
| Endpoint URL | 요청을 받는 https:// URL |
| Event Types | 이 엔드포인트가 받아야 하는 이벤트 |
엔드포인트는 체크한 유형만 받습니다. 아무것도 선택하지 않는 것은 허용되지 않습니다 — 최소 하나는 선택하세요.
서명 비밀키 복사하기
TicketWave는 엔드포인트가 생성되는 순간 서명 비밀키(whsec_…)를 생성합니다. 엔드포인트 목록을 열고 눈 아이콘으로 표시한 뒤, 애플리케이션 설정에 복사하세요.
비밀키는 비밀번호처럼 다루세요. 이 값을 가진 사람은 서명 검사를 통과하는 요청을 위조할 수 있습니다. 저장소에는 절대 넣지 말고 환경 변수에 보관하세요.
테스트 이벤트 보내기
엔드포인트 행의 Send test event 버튼(종이비행기)을 사용하세요. 그러면 "test": true가 포함된 실제 서명 요청이 전송되므로, 실제 티켓이 걸리기 전에 수신기가 제대로 동작하는지 확인할 수 있습니다.
결과는 다른 전달과 마찬가지로 Webhooks 기록에 표시됩니다.
엔드포인트 요구 사항
| Requirement | Detail |
|---|---|
| Scheme | https://만 허용 — http://는 거부됨 |
| Host | 공개적으로 확인 가능해야 합니다. 사설, 루프백, 링크 로컬, CGNAT 주소는 허용되지 않습니다 |
| Response | 어떤 2xx 상태든 성공으로 간주됩니다 |
| Timeout | 응답 시간은 10초입니다 |
| Redirects | 따라가지 않습니다. 3xx는 실패로 간주됩니다 |
| Limit | 서버당 최대 5개 엔드포인트 |
호스트 검사는 엔드포인트를 저장할 때와 모든 전달 직전에 둘 다 수행되므로, 나중에 내부 주소로 해석되기 시작한 도메인에는 더 이상 전달되지 않습니다.
요청
모든 전달은 JSON 본문을 가진 POST입니다.
헤더
| Header | Example | Meaning |
|---|---|---|
Content-Type | application/json | 항상 JSON |
User-Agent | TicketWave-Webhooks/1.0.5 | 요청을 보낸 봇 버전 |
X-TicketWave-Event | ticket.created | 이벤트 유형 |
X-TicketWave-Delivery | wh_3f2a… | 이 전달의 고유 ID |
X-TicketWave-Timestamp | 1786224191 | 서명의 일부인 Unix 초 |
X-TicketWave-Signature | sha256=9f86d0… | 요청의 HMAC |
본문
모든 페이로드는 같은 봉투 형식을 사용합니다. 이벤트 유형마다 data만 다릅니다:
{
"event": "ticket.created",
"timestamp": "2026-08-26T08:23:11.000Z",
"guild_id": "123456789012345678",
"data": {
"ticket_id": "ticket-1042",
"ticket_num_id": 1042,
"channel_id": "998877665544332211",
"category": "🤖 Support",
"user": { "id": "987654321098765432", "username": "Luna" },
"created_at": "2026-08-26T08:23:11.000Z"
}
}모든 유형의 data 객체는 이벤트 레퍼런스를 참고하세요.
서명 검증하기
당신의 엔드포인트 URL을 알아낸 사람은 누구나 POST 요청을 보낼 수 있습니다. 서명은 실제 TicketWave 전달과 위조된 요청을 구분하는 방법입니다.
항상 검증하세요. 검증하지 않은 엔드포인트가 시스템에서 무언가를 생성하거나 닫을 수 있다면, 그건 열린 문과 같습니다.
서명이 만들어지는 방식
TicketWave는 타임스탬프와 원시 요청 본문을 점으로 이어 붙인 뒤, 엔드포인트 비밀키를 사용해 그 결과에 HMAC-SHA256을 적용합니다:
signed_payload = X-TicketWave-Timestamp + "." + raw_request_body
signature = HMAC_SHA256(signed_payload, your_endpoint_secret)헤더에는 그 다이제스트가 16진수로 인코딩되어 접두사와 함께 들어갑니다: sha256=<digest>.
수신기가 해야 할 일
원시 본문을 읽으세요. 받은 정확한 바이트와 대조해야 합니다. 프레임워크가 먼저 JSON을 파싱한 뒤 다시 직렬화하면 키 순서나 공백이 바뀔 수 있고, 그러면 다이제스트가 일치하지 않습니다.
비밀키로 timestamp + "." + rawBody에 대해 HMAC을 다시 계산하세요.
상수 시간으로 비교하세요(Node의 crypto.timingSafeEqual). 단순한 ===는 타이밍 정보를 노출합니다.
타임스탬프가 최근 값인지 확인하세요 — 기본값으로는 5분 정도의 허용 오차가 적당합니다. 타임스탬프는 서명된 페이로드 안에 있으므로, 공격자가 오래된 요청에 새 타임스탬프를 붙여 재전송할 수는 없습니다.
완전한 구현 예시는 Example Server 페이지에 있습니다.
재시도
실패한 전달은 자동으로 재시도됩니다.
| Attempts | 3회(첫 시도 + 재시도 2회) |
| Backoff | 1초, 그다음 5초 |
| Retried on | 네트워크 오류, 타임아웃, 408, 429, 그리고 모든 5xx |
| Not retried on | 그 외 모든 4xx — 이는 엔드포인트가 의도적으로 요청을 거부했다는 뜻입니다 |
재시도 때문에 엔드포인트가 같은 이벤트를 두 번 받을 수 있습니다. X-TicketWave-Delivery를 멱등성 키로 사용하세요: 처리한 ID를 기억하고 중복은 무시하세요.
전달은 순서가 보장되지 않습니다. 두 티켓이 동시에 생성되면 요청이 어느 순서로든 도착할 수 있습니다 — 순서가 중요하다면 본문의 timestamp 필드를 사용하세요.
전달 기록
대시보드의 Webhooks 페이지에는 모든 전달의 상태, 응답 코드, 소요 시간, 시도 횟수가 표시됩니다. 행을 열면 전송된 정확한 요청 페이로드와 서버가 반환한 응답을 볼 수 있습니다.
실패한 전달은 상세 페이지에서 Retry Webhook으로 다시 보낼 수 있습니다. 같은 엔드포인트로 원래 페이로드를 다시 보내며, 새 전달 기록이 생성됩니다.
기록은 30일 동안 보관된 뒤 자동으로 정리됩니다.
문제 해결
| Problem | Fix |
|---|---|
| 엔드포인트를 저장할 수 없음 | URL은 https://여야 하며 공개 주소로 해석되어야 합니다 |
| 응답 코드 없이 모두 실패로 표시됨 | 요청이 아예 도달하지 못한 것입니다 — 타임아웃, DNS 실패 또는 연결 거부 |
| 서명이 절대 일치하지 않음 | 파싱된 본문을 해시하고 있거나 timestamp + "." 접두사를 빼먹은 것입니다 |
| 한동안 전달이 멈춤 | 호스트가 4xx를 반환하기 시작했는지 확인하세요 — 이런 경우는 재시도되지 않습니다 |
| 이벤트가 아예 도착하지 않음 | 엔드포인트가 해당 유형을 구독하지 않았거나, 서버의 프리미엄이 만료된 것입니다 |
| 중복 이벤트 | 재시도 시 예상되는 동작입니다 — X-TicketWave-Delivery로 중복 제거하세요 |
다음 단계
- Event Reference (모든 이벤트와 페이로드)
- Example Server (실행 가능한 Express 수신기)
- Log Channels (같은 이벤트를 Discord에 게시)
How is this guide?
