跳转至主要内容
Certyneo
开发人员的文档

网络网络即时收到签名活动

设置一个HTTPSURL在Certyneo仪表板中, 收到HMAC-SHA256签名的POST, 只要您的信封发生事件:签名,拒绝,到期.

< 5s

事件后的平均交付时间

5x

如果失败,尝试 (直到9小时后)

HMAC-SHA256

每个请求的签名算法

活动目录

下面的8个事件涵盖Certyneo包的整个生命周期. 启动您感兴趣的事件在设置 → Webhooks,忽略其他事件 订阅是事件为小块.

活动发作
envelope.created创建一个包裹 (通过UI,API或模板) 很有用,可以在创建时同步CRM侧记录.
envelope.sent封信发送给签名者 (首次发送电子邮件).标志着活跃签名循环的开始.
envelope.completed已签署的所有签名者. 已封闭的eIDASPDF和审计跟踪可以通过 payload中的 `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`的内容因事件而异 (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"
  }
}

收件符号是UTF-8编码,没有BOM.HMAC签名计算在原始体内,如发送不要改变空格,JSON重新解析经常改变密钥的顺序,破坏验证.

检查HMAC的签名

每个请求都用您的网络连接密码签名 (在创建时只能在仪表板中看到一次,然后在休息时加密).<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`在Node中, `hmac.compare_digest`在Python中).否则,两个签名之间的比较时间差距会逐渐向耐心的攻击者揭示秘密 (时间攻击).

试用政策

如果您的终端返回的不是HTTP 2xx (时间止,5xx,连接被拒绝),我们会按指数式备份响应.共6次尝试,在9小时内.在6次结束后,事件在您的网链仪表板上标记为"失败"",并发送警报电子邮件.

试图时间累计时间过去了
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 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"}'

六个需要遵守的做法

  • 检查HMAC签名在读取任何体内之前 使用时间安全的比较.
  • 处理每个 envelope_id` × `event`一次,存储已见过的ID (回复可以重新交付相同的事件).
  • 答复HTTP200最多5秒,然后处理异步 (队伍).否则您将被计为时间过去了,并重新尝试.
  • ,HMAC检查通常会失败,因为BOM或空白空间是看不见的.
  • 插入一个死亡信件队列,以便在发生事件时,
  • 允许在 `t=`和接收时间之间延迟5分钟,拒绝以阻止重播.

为了更进一步

准备好连接系统吗?

网友会设置可在初始计划 (免费,每月5封信) 中使用.