メインコンテンツへスキップ
Certyneo
開発者資料

ウェブフック リアルタイムでサインアップイベントを受信

Certyneoのダッシュボードに HTTPS URL を設定して,HMAC-SHA256で署名された POST を受信します. 署名,拒否,有効期限のイベントが起きたら, 8 つのイベントがサポートされます. 6 回の試行後に指数値再試行, 8 行の暗号化検証.

< 5s

事件後の平均的な配達時間

5x

失敗した場合は試行 (~9h後まで)

HMAC-SHA256

要求の各項の署名アルゴリズム

事件カタログ

設定 → Webhooks で興味のあるイベントを有効に,他のイベントを無視します 登録はイベントごとに粒状です.

事件発症
envelope.created作成時に CRM側で記録を同期するのに役立つエンベラップが作成されます.
envelope.sent封筒は署名者に送られる (最初のメール送信). 署名サイクルが始まることを意味します.
envelope.completed署名者全員署名しました. eIDASの PDFとトラウルの監査は,ペイロードの `proof_pdf_url`で入手できます.
envelope.declined署名者が封筒を拒否した.拒否理由 (署名者が提供した場合は) は `data.decline_reason` にあります.
envelope.voided封筒は発行者が署名が完了する前にキャンセルされました. `expired` (human vs timeout) とは異なります.
envelope.expired封筒の有効期限は,完全な署名なしで過ぎました. 欠落した署名者のリストは, `data.missing_signers` にあります.
recipient.signed順次ワークフローに役立つ:次の署名者への移行を誘発する.
recipient.viewed署名者が署名しなくても リンクを開けました 標的型再開で便利です

積載物の格式

すべてのイベントは同じトップレベルJSONスケープを共有します: `event`, `envelope_id`, `occurred_at`, `data`. `data`の内容はイベントによって異なります (API参照のイベントごとにドキュメント化されたフィールド). 以下は完全な例です.

{
  "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"
  }
}

payload は UTF-8 でエンコードされ,BOM は含まれていない. HMAC 署名は送信されたままのボディ・ブルートに計算される. 空間を改変しないでください. JSON 再解析はしばしばキー順序を変更し,検証を破ります.

HMACの署名を確認する

ウェブフークの秘密で署名されます (ダッシュボードで作成時に1回だけ表示され,その後休憩時に暗号化されます).署名は, `Certyneo-Signature`ヘッダーで, `t=形式で送信されます.<timestamp>ポイントは<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,接続拒否) 以外の返信をすると,指数的なバックオフで鳴り響く.合計で~9時間以内に 6回試みます. 6回終了すると,Webフックダッシュボードにイベントが `失敗`とマークされ,警告メールが送信されます.

試行錯誤合計期限時間が経った
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

失敗したメールを手動で転送したい場合は Webhook のダッシュボードに 再配信 ボタンが表示されます. 失敗したメールが 返信後 7 日間利用できます.

試しに送るのに 本物の封筒を送らない

`/v1/webhooks/test`のエンドポイントは URLとイベントタイプを受け入れ,実際のものと同じサインの虚構のペイロードを POSTします.継続的な統合やローカル開発 (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の署名をチェックする 身体を読み取る前に タイムセーフの比較をします
  • データベースに既に見たIDを保存して,それぞれの"envelope_id"を"回処理する (リトリープは同じイベントをリデリバーできる).
  • HTTP 200に 5秒以内に返信し,その後アシンクロン処理 (キュー) します. そうでなければタイムアウトして再試行としてカウントされます.
  • 粗質なボディログ + 完全なサインをデバッグする HMAC 検証はしばしばBOMや見えないホワイトスペースで失敗します
  • 事件の"失敗"のデッドレターに 接続して 事件が起きたら 部下に手動で転送します
  • `t=`と受信時間の間の 5分間の遅延を許し,再放送を阻止するために拒否します.

更に進めるために

システム接続準備はいいか?

ウェブフックの設定はスタートアッププランから利用できます (無料,5封筒/月)