본문으로 건너뛰기
LinkProfit

웹훅 연동

서명된 이벤트 전달: 사용할 수 있는 이벤트, 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 }가 담긴 실제 서명 이벤트를 운영 이벤트와 똑같은 경로로 전달합니다. 엔드포인트가 어떻게 응답했는지는 전달 로그에서 확인할 수 있습니다.