웹훅 연동
서명된 이벤트 전달: 사용할 수 있는 이벤트, JavaScript와 Python에서 X-LinkProfit-Signature 검증하기, 재시도와 실패 상태.
2026년 8월 13일 업데이트
웹훅은 이벤트를 JSON POST 요청으로 여러분의 엔드포인트에 보냅니다. 폴링이
필요 없습니다. 엔드포인트는 대시보드(설정 → 웹훅)에서 등록하거나
POST /v1/webhooks로 등록합니다. 서명 시크릿은 생성할 때 한 번만 표시됩니다.
이벤트
| 이벤트 | 발생 시점 | 사용 대상 |
|---|---|---|
| link.created / link.updated / link.deleted | 대시보드, API 또는 일괄 가져오기로 링크가 변경될 때 | 워크스페이스 및 파트너 엔드포인트 |
| domain.pending_ssl | DNS 확인이 끝나 인증서를 발급하는 중일 때 | 워크스페이스와 파트너 |
| domain.activated | 도메인이 트래픽을 처리하기 시작할 때 | 워크스페이스와 파트너 |
| domain.error | 프로비저닝에 실패했을 때. 상세 내용이 함께 담깁니다 | 워크스페이스와 파트너 |
| click.threshold | 월간 추적 클릭 수 한도의 80% / 100%에 도달했을 때 | 워크스페이스와 파트너 |
| conversion.created | 전환이 기록되었을 때. 클릭에 귀속된 주문, 가입 또는 사용자 지정 목표입니다 | 워크스페이스와 파트너 |
| workspace.subscribed | 고객이 구독의 첫 청구서를 결제했을 때 | 파트너 전용 |
| workspace.past_due | 고객의 결제가 실패했을 때 | 파트너 전용 |
| workspace.suspended | 고객이 정지되었을 때(미납 또는 수동 정지) | 파트너 전용 |
| payment.succeeded | 고객의 결제가 정산되었을 때. 수수료 내역이 함께 담깁니다 | 파트너 전용 |
| payment.refunded | 결제가 환불되었을 때 | 파트너 전용 |
전달 형식
POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-LinkProfit-Event: link.created
X-LinkProfit-Delivery: dlv_8Yq2…
X-LinkProfit-Signature: t=1755081600,v1=5f8a2c…
{
"id": "dlv_8Yq2…",
"type": "link.created",
"created_at": "2026-08-13T10:00:00.000Z",
"data": { "id": "lnk_…", "domain": "go.your-brand.com", "slug": "x7f2q1z", "url": "https://example.com" }
}
10초 안에 2xx 계열 상태 코드로 응답하십시오. 시간 초과를 포함한 그 밖의
모든 응답은 실패로 간주되어 재시도가 예약됩니다. 전달은 순서가 뒤바뀌어
도착할 수 있고 드물게는 두 번 도착하기도 합니다. id를 기준으로 중복을
제거하십시오.
서명 검증
서명은 엔드포인트 시크릿으로 `${t}.${rawBody}`에 대해 계산한
HMAC-SHA256이며 16진수로 인코딩됩니다. 검증은 언제나 JSON을 파싱하기 전에
원본 요청 본문을 대상으로 수행하고, 5분이 지난 타임스탬프는 거부하십시오.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifySignature(secret, rawBody, header, toleranceSeconds = 300) {
const parts = Object.fromEntries(
header.split(",").map((piece) => piece.split("=", 2)),
);
if (!parts.t || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1);
return a.length === b.length && timingSafeEqual(a, b);
}
import hashlib
import hmac
import time
def verify_signature(secret: str, raw_body: bytes, header: str, tolerance: int = 300) -> bool:
parts = dict(piece.split("=", 1) for piece in header.split(",") if "=" in piece)
timestamp, signature = parts.get("t"), parts.get("v1")
if not timestamp or not signature:
return False
if abs(time.time() - int(timestamp)) > tolerance:
return False
expected = hmac.new(
secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
재시도와 실패 상태
첫 시도는 즉시 이루어집니다. 실패한 전달은 정해진 일정에 따라 재시도합니다.
1분 → 5분 → 30분 → 2시간 → 12시간. 다섯 번째 재시도 이후 엔드포인트는
failing으로 표시되어 새 이벤트를 더 받지 않으며, 소유자에게 이메일이
발송됩니다.
엔드포인트를 고친 뒤 대시보드에서 다시 활성화하거나
PATCH /v1/webhooks/{id}에 {"status": "active"}를 보내십시오. 놓친 이벤트는
자동으로 다시 전달되지 않습니다. 전달 로그
(GET /v1/webhooks/{id}/deliveries 또는 대시보드)에 모든 페이로드가 남아
있으므로 대조해서 보정할 수 있습니다.
전달에 한 번 성공하면 연속 실패 횟수가 초기화됩니다.
테스트
테스트 전송 버튼(또는 POST /v1/webhooks/{id}/test)은
"data": { "test": true }가 담긴 실제 서명 이벤트를 운영 이벤트와 똑같은
경로로 전달합니다. 엔드포인트가 어떻게 응답했는지는 전달 로그에서 확인할 수
있습니다.