跳转至主要内容
Certyneo
公开 API v1

将电子签名集成到您的技术栈

发送信封、跟踪签名、接收 webhooks。简单的 REST API、OpenAPI 3.0、curl/Node/Python 示例 — 所有内容都可在数小时内将 Certyneo 连接到您的 HRIS、CRM 或业务软件。

快速开始

三个步骤:从参数设置中创建 API 密钥,将您的 PDF 编码为 base64,然后发送。响应包含 `signUrl`,您可以直接与收件人分享。

cURLbash
# 1. Upload the PDF (multipart) and capture the returned document id.
DOC_ID=$(curl -s -X POST https://certyneo.com/api/v1/documents \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "file=@contrat.pdf" | jq -r .id)

# 2. Create a DRAFT envelope referencing the uploaded document.
ENV_ID=$(curl -s -X POST https://certyneo.com/api/v1/envelopes \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d "{
    \"subject\": \"Contrat de prestation\",
    \"documentIds\": [\"$DOC_ID\"],
    \"recipients\": [
      { \"email\": \"client@example.com\", \"name\": \"Marie Dubois\", \"role\": \"SIGNER\" }
    ]
  }" | jq -r .id)

# 3. Dispatch the envelope — this sends the invitation email/SMS.
curl -X POST https://certyneo.com/api/v1/envelopes/$ENV_ID/send \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
JavaScript / Nodets
// Plain fetch, no SDK to install.
const auth = { Authorization: `Bearer ${process.env.CERTYNEO_API_KEY}` };

// 1. Upload the PDF (multipart).
const fd = new FormData();
fd.append("file", new Blob([pdfBuffer], { type: "application/pdf" }), "contrat.pdf");
const doc = await fetch("https://certyneo.com/api/v1/documents", {
  method: "POST", headers: auth, body: fd,
}).then((r) => r.json());

// 2. Create the DRAFT envelope.
const envelope = await fetch("https://certyneo.com/api/v1/envelopes", {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({
    subject: "Contrat de prestation",
    documentIds: [doc.id],
    recipients: [
      { email: "client@example.com", name: "Marie Dubois", role: "SIGNER" },
    ],
  }),
}).then((r) => r.json());

// 3. Dispatch — this triggers the invitation channel for every recipient.
await fetch(`https://certyneo.com/api/v1/envelopes/${envelope.id}/send`, {
  method: "POST", headers: auth,
});
console.log(envelope.id);
Pythonpython
import os, requests

auth = {"Authorization": f"Bearer {os.environ['CERTYNEO_API_KEY']}"}

# 1. Upload the PDF (multipart).
with open("contrat.pdf", "rb") as f:
    doc = requests.post(
        "https://certyneo.com/api/v1/documents",
        headers=auth,
        files={"file": ("contrat.pdf", f, "application/pdf")},
    ).json()

# 2. Create the DRAFT envelope.
envelope = requests.post(
    "https://certyneo.com/api/v1/envelopes",
    headers={**auth, "Content-Type": "application/json"},
    json={
        "subject": "Contrat de prestation",
        "documentIds": [doc["id"]],
        "recipients": [
            {"email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER"},
        ],
    },
).json()

# 3. Dispatch — this triggers the invitation channel for every recipient.
requests.post(
    f"https://certyneo.com/api/v1/envelopes/{envelope['id']}/send",
    headers=auth,
)
print(envelope["id"])

从您的工具尝试 API

Postman 集合和 RapidAPI 卡片从记录此页面的 OpenAPI 规范生成。这三者保持与真实 API 对齐,逐端点对齐,而不是在第一次添加时分散。

Collection Postman

Les 25 requêtes rangées par domaine — enveloppes, documents, modèles, cachets, webhooks — avec un exemple de corps et de réponse pour chacune. Collez votre clé dans la variable apiKey de la collection, puis lancez GET /health : elle ne demande aucune authentification et confirme que votre configuration est bonne avant le premier appel authentifié.

Fiche RapidAPI

Le même catalogue d'endpoints, essayable directement depuis le navigateur. Le banc d'essai attend deux en-têtes distincts : la clé RapidAPI que la plateforme vous attribue, et votre clé Certyneo dans Authorization — c'est la seconde qui autorise réellement l'appel.

信封

创建、发送、状态跟踪、取消。一个信封可以包含多个文档和多个签署人(并行或顺序)。

网络钩子

Tous les événements d'enveloppe et de destinataire (`envelope.sent`, `recipient.signed`, `envelope.completed`…) livrés sur l'URL de votre choix — liste complète sur /developers/webhooks. HMAC SHA-256 sur chaque payload pour vérifier l'origine.

简单身份验证

Bearer 令牌。每个环境一个密钥(测试/生产)。可以立即撤销。限制为每个密钥每分钟 100 个请求,突发 200 个,干净的 429 响应带有 Retry-After 标头。

可用端点

L'ensemble des routes publiques : compte, documents, enveloppes, modèles, webhooks, cachets électroniques, clés API et facturation. Toutes acceptent un Bearer token et renvoient du JSON.

MethodPathDescription
GET/api/v1/account/meIdentity of the authenticated caller (id, email, plan) — scope-less credential probe
POST/api/v1/documentsUpload a PDF (multipart) — returns document id
GET/api/v1/documentsList documents
GET/api/v1/documents/{id}Fetch document metadata
DELETE/api/v1/documents/{id}Delete document
GET/api/v1/envelopesList envelopes (filter with ?status= and ?limit=)
POST/api/v1/envelopesCreate envelope (status: DRAFT) — from a templateId, or from documentIds with an optional fields array
GET/api/v1/envelopes/{id}Fetch envelope state
PATCH/api/v1/envelopes/{id}Update DRAFT envelope
DELETE/api/v1/envelopes/{id}Void / delete DRAFT envelope
POST/api/v1/envelopes/{id}/sendDispatch DRAFT — sends invitations
GET/api/v1/envelopes/{id}/audit-trailDownload eIDAS audit-trail PDF
GET/api/v1/audit-anchors/{root}Public: metadata of an anchored audit batch, or the .ots proof file with ?format=ots (no auth)
POST/api/v1/audit-anchors/verifyPublic: verify an audit entry + proof against its anchored Merkle root (no auth, reveals nothing)
GET/api/v1/envelopes/{id}/signed-documentDownload signed PDF (once COMPLETED)
GET/api/v1/envelopes/bulkList your bulk-send jobs
POST/api/v1/envelopes/bulkBulk send: create N envelopes from one template + a CSV (Standard/Business only)
GET/api/v1/envelopes/bulk/{id}Bulk-send job progress: counts, per-row failures, created envelopes
GET/api/v1/templatesList reusable envelope templates
POST/api/v1/templatesCreate a template (documents, roles, positioned fields)
POST/api/v1/sepa-mandatesGenerate a SEPA mandate PDF and its DRAFT envelope, fields already placed
POST/api/v1/payroll-adapters/normalizeNormalise a payroll CSV (Silae, Sage Paie, PayFit, Lucca) into the bulk-send shape
POST/api/v1/ag-coproprieteCreate a condominium general-meeting envelope (resolutions + ownership shares)
POST/api/v1/ag-copropriete/{envelopeId}/votesRecord a co-owner's votes on the meeting resolutions
POST/api/v1/ag-copropriete/{envelopeId}/tallyTally the meeting - per-resolution result weighted by ownership shares
GET/api/v1/videos/{videoId}Download a stored identity video - GDPR art. 15 access path
GET/api/v1/webhooksList webhooks
POST/api/v1/webhooksRegister webhook — returns the signing secret once
GET/api/v1/webhooks/{id}Fetch webhook subscription
PATCH/api/v1/webhooks/{id}Update url / events / active state
DELETE/api/v1/webhooks/{id}Unregister
POST/api/v1/sealsApply a qualified electronic seal to a document
GET/api/v1/seals/{id}Fetch seal status
GET/api/v1/seals/{id}/certificateDownload the seal certificate
GET/api/v1/keysList API keys
POST/api/v1/keysCreate API key — the secret is shown once
PATCH/api/v1/keys/{id}Rename / revoke key
DELETE/api/v1/keys/{id}Delete key
GET/api/v1/billing/usageCurrent period usage and projected cost
GET/api/v1/statusService status
GET/api/v1/openapiMachine-readable OpenAPI specification

Modèles d'enveloppe

Un modèle enregistre une fois pour toutes le PDF, les rôles et l'emplacement des champs de signature, puis se réutilise à chaque envoi : passez son identifiant dans templateId au lieu de documentIds, et les champs positionnés sont recopiés sur l'enveloppe créée.

  • Les modèles se créent depuis le tableau de bord (Modèles → Nouveau modèle), où vous déposez le document puis positionnez les champs à la souris — c'est le chemin le plus simple. L'API permet aussi de les créer par POST /api/v1/templates, en fournissant les documents, les rôles et les champs ; attention, les champs y sont positionnés en coordonnées absolues (page, x, y, largeur, hauteur), il faut donc connaître la mise en page du PDF.
  • Sur un compte qui n'a encore enregistré aucun modèle, GET /api/v1/templates renvoie une liste vide. C'est le comportement normal, pas un défaut d'authentification.
  • La liste ne contient que les modèles appartenant à l'utilisateur propriétaire de la clé API. Un modèle créé par un collègue n'y figure pas, même au sein d'un espace de travail partagé : générez la clé depuis le compte qui possède le modèle.
  • templateId et documentIds s'excluent mutuellement : envoyez l'un ou l'autre, jamais les deux ni aucun des deux.
  • Fournissez au moins autant de destinataires SIGNER que le modèle compte de rôles signataires, sinon la création est refusée en 400. Le champ signerCount renvoyé par la liste indique le nombre attendu.
  • Le niveau de signature du modèle est hérité par l'enveloppe, sauf si la requête passe explicitement signatureLevel.
cURLbash
# 1. Discover the templates saved on this account.
curl -s https://certyneo.com/api/v1/templates \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

# {
#   "data": [
#     { "id": "cmdq7f4k80001s6y2h1xa9pl3", "name": "Contrat de prestation",
#       "signerCount": 2, "documentCount": 1, "signatureLevel": "SIMPLE" }
#   ],
#   "pagination": { "page": 1, "pageSize": 20, "total": 1, "pages": 1 }
# }
#
# An empty "data" array means no template exists on this account yet —
# create one from the dashboard, it is not an authentication problem.

# 2. Create the envelope FROM the template: no documentIds and no field
#    coordinates, both are carried by the template. Pass one SIGNER
#    recipient per signer role, in the template's role order.
curl -X POST https://certyneo.com/api/v1/envelopes \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Contrat de prestation",
    "templateId": "cmdq7f4k80001s6y2h1xa9pl3",
    "recipients": [
      { "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" },
      { "email": "legal@example.com", "name": "Paul Martin", "role": "SIGNER" }
    ]
  }'

# 3. The envelope is DRAFT at this point — POST /envelopes/{id}/send
#    dispatches it, exactly as in the quick-start above.

Depuis Power Automate ou Zapier

L'action « Créer une enveloppe » de nos connecteurs no-code ne couvre que le chemin par modèle : le champ Modèle y est obligatoire. Pour un document ad hoc qui change à chaque exécution, utilisez l'action « Téléverser un document » puis une action HTTP brute vers POST /api/v1/envelopes en passant le documentIds retourné.

文档发送:接受两种形式

POST /api/v1/documents 接受文件的两种方式(可任选)。两种情况下适用相同的控制:允许的类型、50 MB 上限、二进制签名验证和防病毒分析。

  • 以 multipart/form-data 形式,使用名为 file 的部分。这是经典形式,也是 curl -F 和大多数库的形式。
  • 原始正文:文件的字节构成请求正文,Content-Type 标头给出其类型(例如 application/pdf)。从传递内容原样而不包装请求的工具中很有用——这就是 Power Automate 连接器所做的。
  • 在原始正文中,文件名在正文中没有位置:通过 X-File-Name 标头或 ?fileName= 参数指定它。没有它,文档按其类型命名。
  • 不支持的类型以 415 响应,并命名两种接受的形式;声称为 multipart 但无法读取的正文以 400 响应。两者都不是服务器错误。
cURLbash
# a. multipart/form-data — the classic shape.
curl -X POST https://certyneo.com/api/v1/documents \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "file=@contrat.pdf;type=application/pdf"

# b. raw body — the file bytes ARE the body, typed by Content-Type.
#    The filename has nowhere to live in the body, so pass it as a header
#    (or ?fileName=). Without it the document is named after its type.
curl -X POST https://certyneo.com/api/v1/documents \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/pdf" \
  -H "X-File-Name: contrat.pdf" \
  --data-binary "@contrat.pdf"

# Both return the same 201 with the document id to pass as documentIds.

签名字段的放置

在没有模板的情况下,从 documentIds 创建的信封没有预先定位的字段:签署人收到的文档中没有签名位置。fields 数组在同一创建调用中传递,精确放置每个字段——这是 API 端的等效操作,相当于模板一次性记录的内容。

  • 坐标以 PDF 点为单位,原点在页面左上角,Y 轴向下(A4 页面尺寸为 595 × 842 点)。x 和 y 指定字段的左上角,width 和 height 指定其大小。
  • pageNumber 从 1 开始,documentIndex 从 0 开始。超出文档范围的页码在创建时不会被拒绝:该字段在签署时被忽略,不会出现在任何地方——当字段缺失时,这是首先要检查的地方。
  • recipientEmail 必须与同一调用中的一个收件人匹配,不区分大小写。否则创建会失败,并列出所有有问题的行,这样可以避免逐个更正。
  • fields 和 templateId 相互排斥:模板已经具有自己的布局。因此 fields 数组仅与 documentIds 一起使用。
  • 接受的类型:SIGNATURE、INITIALS、DATE_SIGNED、TEXT、CHECKBOX 和 RADIO_GROUP。required 默认为 true;placeholder 和 dateFormat 是可选的,options 仅对 RADIO_GROUP 进行处理。
  • 一个信封最多接受 100 个字段、20 个文档和 50 个收件人——您的计划限制可能更低。
cURLbash
# Ad-hoc envelope WITH pre-placed fields — no template involved.
# Coordinates are PDF points, origin TOP-LEFT of the page, +Y downwards
# (A4 = 595 x 842 pt). x / y are the field box's top-left corner.
#
# The last field is positioned by ANCHOR instead of by eye: the server
# locates "Signature du client" in the PDF and computes the spot. x / y
# stay required there — they are the fallback if the text is not found.
curl -X POST https://certyneo.com/api/v1/envelopes \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Contrat de prestation",
    "documentIds": ["cmdq7f4k80001s6y2h1xa9pl3"],
    "recipients": [
      { "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" }
    ],
    "fields": [
      { "recipientEmail": "client@example.com", "documentIndex": 0,
        "pageNumber": 2, "fieldType": "SIGNATURE",
        "x": 90, "y": 640, "width": 180, "height": 44 },
      { "recipientEmail": "client@example.com", "documentIndex": 0,
        "pageNumber": 2, "fieldType": "DATE_SIGNED",
        "x": 320, "y": 640, "width": 140, "height": 30,
        "dateFormat": "DD/MM/YYYY" },
      { "recipientEmail": "client@example.com", "documentIndex": 0,
        "pageNumber": 2, "fieldType": "TEXT",
        "x": 90, "y": 700, "width": 200, "height": 30,
        "placeholder": "Fonction", "required": false },
      { "recipientEmail": "client@example.com", "documentIndex": 0,
        "pageNumber": 2, "fieldType": "SIGNATURE",
        "anchorText": "Signature du client", "anchorPlacement": "below",
        "anchorIndex": 0,
        "x": 90, "y": 640, "width": 180, "height": 44 }
    ]
  }'

# The envelope is DRAFT at this point — POST /envelopes/{id}/send
# dispatches it, exactly as in the quick-start above.

文本锚点:在不知道坐标的情况下放置字段

字段可以引用打印在文档中的文本,而不是坐标:anchorText 在 PDF 中查找它,服务器在创建时计算位置。当文档在每次发送时重新生成时,这是首选方法——邮件合并、合同生成器——因为布局会变化,但"客户签名"的措辞仍然保留。anchorPlacement 表示字段相对于文本的哪一侧放置(默认为 right,也可以是 below、above 或 left),anchorIndex 在文本出现多次时选择出现次数。

即使使用锚点,x 和 y 仍然是必需的:它们充当备用值。找不到锚点不会导致创建失败——该字段保留您提供的字面位置,响应中没有错误或警告。因此请提供可能的备用值而不是 0,0,并在首次发送时验证呈现。

Authentification

Chaque appel porte une clé API dans l'en-tête Authorization. Les clés se génèrent depuis Réglages → Clés API et ne sont affichées qu'une seule fois.

HTTPhttp
GET /api/v1/account/me HTTP/1.1Host: certyneo.com
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx

# 200 OK
{ "data": { "id": "usr_…", "email": "you@example.com", "plan": "BUSINESS", "environment": "live" } }
  • Format : sk_live_… en production, sk_test_… pour le bac à sable. En-tête : Authorization: Bearer <clé>.
  • Portées : envelopes, documents, webhooks, seals — en lecture (:read) ou écriture (:write). L'écriture implique la lecture ; la portée * donne tous les droits.
  • sk_test_ 密钥创建的沙盒资源不计入配额和账单。除非收件人地址与发送账户自己的邮箱一致,否则不会发送真实邮件——这有助于在自己身上测试完整流程。
  • Erreurs : 401 clé invalide, 403 portée insuffisante, 429 limite de débit dépassée, 402 quota mensuel atteint.

Forme des réponses

Un point à connaître avant d'écrire votre client : les collections sont encapsulées dans un objet data, alors que les ressources unitaires sont renvoyées à plat. Lire response.data.data sur une ressource unitaire renvoie donc undefined.

Collection — encapsuléejson
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
  "data": [
    { "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
  ]
}
单项资源 — 平面json
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* … */ ]
}

速率限制

速率限制保证所有客户的服务质量稳定。如果您需要更多,请联系我们。

  • 每个 API 密钥每分钟 100 个请求
  • 允许突发到 10 秒内的 200 个请求
  • 429 响应,带有指示延迟秒数的 Retry-After 标头