웹후크 실시간 서명 이벤트를 받습니다
Certyneo 대시보드에서 HTTPS URL을 설정하고, HMAC-SHA256로 서명된 POST을 수신합니다. 서면에 어떤 이벤트가 발생하면, 서면, 거부, 종료. 8개의 이벤트가 지원됩니다. 6번 시도에 대해 배수적인 재시험, 8줄의 암호 검증.
< 5s
사건 이후의 평균 배달 기간
5x
실패 시도 (~ 9시간 후까지)
HMAC-SHA256
각 요청에 대한 서명 알고리즘
이벤트 목록
아래 8개의 이벤트는 Certyneo 봉투의 전체 라이프 사이클을 포함합니다. 설정을 통해 관심 있는 이벤트를 활성화하세요 → 웹후크, 다른 이벤트를 무시하세요
| 사건 | 발발 |
|---|---|
envelope.created | CRM 측 기록을 생성 시 동기화하는데 유용합니다. |
envelope.sent | 봉투는 서명자에게 보내집니다. 첫 번째 이메일 전송. 활성 서명 순환의 시작을 표시합니다. |
envelope.completed | 모든 서명자들이 서명했습니다. eIDAS 밀폐 PDF와 오디트 트레일은 페이로드에서 `proof_pdf_url`로 사용할 수 있습니다. |
envelope.declined | 서명자가 봉투를 거부했습니다. 거부 이유 (서명자가 제공하면) 는 `data.decline_reason`에 있습니다. |
envelope.voided | 양봉은 발급자가 완전히 서명하기 전에 취소되었습니다. |
envelope.expired | 봉투의 유효기간은 완전히 서명되지 않은 채 지나갔어요. 사라진 서명자의 목록은 `data.missing_signers`에 있습니다. |
recipient.signed | 개별 서명자가 서명했습니다 (하지만 모든 것이 아닙니다). 순차적인 작업 흐름에 유용합니다. 다음 서명자에게 이동을 시작하십시오. |
recipient.viewed | 한 서명자가 서명하지 않고서도 서명 링크를 열었습니다. |
페이로드 형식
모든 이벤트들은 같은 상위 레벨 JSON 패턴을 공유합니다: `event`, `envelope_id`, `occurred_at`, `data`. `data`의 내용은 이벤트에 따라 다릅니다.
{
"event": "envelope.completed",
"envelope_id": "env_01HG3K8X9Y2N4P5Q6R7S8T9V0W",
"occurred_at": "2026-05-27T08:42:13.521Z",
"data": {
"title": "Contrat de prestation Acme Corp",
"status": "completed",
"created_at": "2026-05-24T14:12:00.000Z",
"completed_at": "2026-05-27T08:42:13.000Z",
"signers": [
{
"email": "client@acme.example",
"name": "Alex Client",
"signed_at": "2026-05-27T08:42:13.000Z",
"method": "eidas_aes",
"ip": "203.0.113.42"
}
],
"proof_pdf_url": "https://certyneo.com/api/envelopes/env_01HG.../proof.pdf"
}
}페이로드는 BOM 없이 UTF-8로 암호화된다. HMAC 서명은 전송된 바와 같이 의 원형에 계산된다.
HMAC 서명 확인
각 요청은 웹후크 비밀로 서명됩니다. (다시보드에서 생성할 때 한 번만 볼 수 있고, 휴식시엔 암호화됩니다.) 서명은 `Certyneo-Signature` 헤더에서 `t= 형식으로 전송됩니다.<timestamp>v1=<hex_hmac>`. 페이로드 처리 전에 항상 서명 확인하세요. 이 단계 없이는 누구나 이벤트를 만들어서 엔드포인트에 호출할 수 있습니다.
Node.js / TypeScript
import crypto from "node:crypto";
export function verifyCertyneoSignature(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
// Header format: t=<timestamp>,v1=<hex_hmac>
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=")),
);
const ts = parts.t;
const sig = parts.v1;
if (!ts || !sig) return false;
// Reject events older than 5 minutes (replay protection).
const age = Math.abs(Date.now() / 1000 - Number(ts));
if (age > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
// timingSafeEqual to mitigate timing attacks.
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex"),
);
}Python
import hashlib
import hmac
import time
def verify_certyneo_signature(
raw_body: bytes, signature_header: str, secret: str
) -> bool:
"""Verify a Certyneo webhook signature.
Header format: `t=<timestamp>,v1=<hex_hmac>`
Rejects events older than 5 minutes (replay protection).
"""
parts = dict(p.split("=") for p in signature_header.split(","))
ts = parts.get("t")
sig = parts.get("v1")
if not ts or not sig:
return False
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(
secret.encode("utf-8"),
f"{ts}.{raw_body.decode('utf-8')}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)흔히 발생하는 실수
기대된 서명과 받은 서명을 비교하기 위해 `===` 또는 `==`를 사용하지 마십시오. 시간 안전 함수 (`crypto.timingSafeEqual` in Node, `hmac.compare_digest` in Python) 를 사용하십시오. 그렇지 않으면 두 서명 사이의 비교 시간 간격이 침략자에게 비밀을 점차적으로 공개합니다.
재검토 정책
만약 여러분의 엔드포인트가 HTTP 2xx (시간정지, 5xx, 연결 거부) 를 제외한 다른 응답을 한다면, 우리는 기하급수적인 백업으로 응답합니다. 총 6번 ~9시간 동안 시도합니다. 6번을 마친 후에는 웹 탭에서 이벤트가 `실패`로 표시되고 경고 이메일이 전송됩니다.
| 시도 | 합쳐진 기간 | 시간이 지났어 |
|---|---|---|
| #1 | 0 | 0 |
| #2 | + 1 min | 1 min |
| #3 | + 5 min | 6 min |
| #4 | + 30 min | 36 min |
| #5 | + 2 h | 2h 36 |
| #6 | + 6 h | 8h 36 |
만약 당신이 실패한 이벤트를 수동으로 전달하고 싶다면, 웹허크 대시보드에는 실패한 모든 배송에 대해 7일 동안 사용할 수 있는 재배송 버튼이 있습니다.
실제 봉투를 보내지 않고 테스트하는 것
엔드포인트 `/v1/webhooks/test`는 URL과 이벤트 타입을 받아, 실제와 정확히 서명된 가상의 페이로드를 POSTe합니다. 지속적인 통합 또는 로컬 개발 (ngrok 또는 동등한) 도중 핸들을 검증하는 데 이상적입니다.
# Trigger a test webhook to your endpoint
curl -X POST https://api.certyneo.com/v1/webhooks/test \
-H "Authorization: Bearer $CERTYNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"event": "envelope.completed", "url": "https://your.app/webhooks/certyneo"}'6가지 실천방법
- HMAC 서명을 확인 하려면 BEFORE 모든 본격적인 읽기 시간 안전 비교를 사용 하 여
- 이미 본 ID를 저장하여 하나의 `envelope_id` × `event`를 처리합니다. (회복은 같은 이벤트를 다시 제공 할 수 있습니다.)
- HTTP 200에 5초 이내에 응답하고, 그 다음 무동속으로 처리합니다. 그렇지 않으면 타임아웃이 되고 다시 시도로 간주됩니다.
- 본체 록 + 디버깅에 대한 완전한 서명 HMAC 검사는 종종 BOM 또는 보이지 않는 화면공간에 실패합니다.
- `실패' 이벤트에 드레터 대기열을 연결해서
- `t=`와 수신시간 사이 5분 간격을 허용하고, 리플레이를 차단하기 위해 거부합니다.
더 나아가기 위해
시스템 연결 준비 됐어?
2분 안에 무료 계정을 만들 수 있습니다. 웹후크 설정은 스타터 계획에서 사용할 수 있습니다. (무료, 5개 봉투/달).