콘텐츠로 이동

Webhook

Webhook을 사용하면 콘텐츠 발행 성공/실패, 답글 완료, 플랫폼 알림 등 비동기 작업의 결과를 실시간 HTTP POST 요청으로 수신할 수 있습니다.


메서드경로설명필요 스코프
POST/v1/brand/webhooks/subscriptions새 Webhook 엔드포인트 등록webhooks:write 또는 *
GET/v1/brand/webhooks/subscriptions등록된 Webhook 구독 목록 조회webhooks:read 또는 *
DELETE/v1/brand/webhooks/subscriptions/{id}Webhook 구독 삭제webhooks:write 또는 *

이벤트명발생 조건
sns.publish.succeededSNS 게시물이 대상 플랫폼에 성공적으로 등록됨
sns.publish.failedSNS 게시물 등록이 최종 실패함 (에러 코드 및 재시도 여부 포함)
sns.publish.blocked_entitlement요금제 한도 초과 또는 플랜 만료로 발행이 차단됨
sns.reply.succeeded댓글에 대한 답글 작성이 대상 플랫폼에 등록됨
sns.reply.failed답글 작성이 실패함
sns.provider.notification_receivedSNS 플랫폼으로부터 실시간 피드/웹훅 알림을 수신함

📦 Webhook 페이로드 예시 (sns.publish.succeeded)

섹션 제목: “📦 Webhook 페이로드 예시 (sns.publish.succeeded)”
{
"event_id": "evt_01jm8za2example",
"event_type": "sns.publish.succeeded",
"schema_version": 1,
"occurred_at": "2026-08-16T03:30:00Z",
"producer": "ankk.sns",
"brand_ref": "brand_01jm8v4k9example",
"idempotency_key": "launch-2026-001",
"trace_id": "trc_9a8b7c6d",
"data": {
"content_id": "content_01jm8za2example",
"publish_job_id": "job_01jm8za2jobexample",
"sns_type": "instagram",
"connection_id": "conn_01jm8x9k2example",
"provider_post_id": "18029384756102938",
"permalink": "https://www.instagram.com/p/C-example/"
}
}

모든 Webhook 요청에는 위변조를 방지하기 위해 다음 헤더가 포함됩니다:

  • Social-Webhook-Signature: 서명 값 (t=1723780000,v1=hex_signature)
  • Social-Webhook-Timestamp: 전송 타임스탬프 (Unix 초 단위)
  • Social-Webhook-Id: 이벤트 고유 식별자
import crypto from 'node:crypto';
export function verifyWebhookSignature(
rawBody: string,
signatureHeader: string,
timestampHeader: string,
secret: string
): boolean {
const signedPayload = `${timestampHeader}.${rawBody}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// constant-time 비교로 타이밍 공격 방지
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expectedSignature)
);
}
import hmac
import hashlib
def verify_webhook_signature(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
signed_payload = f"{timestamp}.".encode('utf-8') + raw_body
expected = hmac.new(secret.encode('utf-8'), signed_payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)

[!CAUTION] 서명 검증 시 반드시 JSON 파싱 이전의 **원본 바디(Raw Body)**를 사용해야 합니다.