Перейти к основному содержимому
Certyneo
Публичное API v1

Интегрируйте электронную подпись в вашу систему

Отправляйте конверты, отслеживайте подписи, получайте вебхуки. Простое 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, endpoint за endpoint, вместо того чтобы расходиться при первом добавлении.

Коллекция Postman

25 запроса, организованные по доменам — конверты, документы, шаблоны, печати, webhooks — с примером тела и ответа для каждого. Вставьте ваш ключ в переменную apiKey коллекции, затем запустите GET /health: она не требует аутентификации и подтверждает, что ваша конфигурация правильная перед первым аутентифицированным вызовом.

Карточка RapidAPI

Тот же каталог endpoints, тестируемый прямо из браузера. Тестовая площадка ожидает два отдельных заголовка: ключ RapidAPI, который вам назначила платформа, и ваш ключ Certyneo в Authorization — второй фактически авторизирует вызов.

Конверты

Создание, отправка, отслеживание статуса, отмена. Конверт может содержать несколько документов и несколько подписантов (параллельно или последовательно).

Вебхуки

Все события конверта и получателя (`envelope.sent`, `recipient.signed`, `envelope.completed`…) доставляются на выбранный вами URL — полный список на /developers/webhooks. HMAC SHA-256 для каждого payload для проверки источника.

Простая аутентификация

Bearer token. Один ключ на окружение (test / prod). Можно отозвать мгновенно. Лимит 100 запросов/мин/ключ, всплеск до 200, чистый ответ 429 с заголовком Retry-After.

Доступные endpoints

Все общедоступные маршруты: учетная запись, документы, конверты, шаблоны, вебхуки, электронные печати, ключи API и выставление счетов. Все принимают Bearer token и возвращают 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

Шаблоны оболочек

Шаблон раз и навсегда сохраняет PDF, роли и расположение полей подписи, а затем переиспользуется при каждой отправке: передайте его идентификатор в templateId вместо documentIds, и позиционированные поля скопируются на созданный конверт.

  • Шаблоны создаются с панели управления (Шаблоны → Новый шаблон), где вы загружаете документ и позиционируете поля мышью — это самый простой способ. API также позволяет создавать их через POST /api/v1/templates, указав документы, роли и поля; помните, что поля в нём позиционируются абсолютными координатами (страница, x, y, ширина, высота), поэтому необходимо знать макет PDF.
  • На аккаунте, который ещё не сохранил ни одного шаблона, GET /api/v1/templates возвращает пустой список. Это нормальное поведение, а не ошибка аутентификации.
  • Список содержит только шаблоны, принадлежащие пользователю-владельцу ключа API. Шаблон, созданный коллегой, в нём не отображается, даже в рамках общей рабочей области: сгенерируйте ключ с аккаунта, которому принадлежит шаблон.
  • templateId и documentIds взаимно исключают друг друга: передайте один из них, но не оба и не ни один.
  • Предоставьте как минимум столько же получателей SIGNER, сколько ролей подписантов в шаблоне, иначе создание будет отклонено с кодом 400. Поле signerCount, возвращаемое списком, указывает ожидаемое количество.
  • Уровень подписи шаблона наследуется конвертом, кроме случаев, когда запрос явно передаёт 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.

Из Power Automate или Zapier

Действие «Создать конверт» в наших no-code коннекторах охватывает только путь через шаблон: поле Шаблон в нём обязательно. Для документа ad hoc, который меняется при каждом выполнении, используйте действие «Загрузить документ», а затем необработанное HTTP-действие на POST /api/v1/envelopes, передав возвращённый documentIds.

Отправка документа: две принятые формы

POST /api/v1/documents принимает файл двумя способами на выбор. Одинаковые проверки применяются в обоих случаях: разрешённые типы, лимит 50 МБ, проверка бинарной подписи и антивирусный анализ.

  • В 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 и проверьте результат при первой отправке.

Аутентификация

Каждый вызов содержит ключ API в заголовке Authorization. Ключи генерируются из Параметры → Ключи API и отображаются только один раз.

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" } }
  • Формат: sk_live_… в production, sk_test_… для sandbox. Заголовок: Authorization: Bearer <ключ>.
  • Области: envelopes, documents, webhooks, seals — для чтения (:read) или записи (:write). Запись подразумевает чтение; область * предоставляет все права.
  • Ключи sk_test_ создают ресурсы в песочнице, исключённые из квоты и выставления счетов. Получателю не отправляется настоящее письмо, если только его адрес не совпадает с email отправляющего аккаунта — удобно для тестирования всего процесса на себе.
  • Ошибки: 401 неверный ключ, 403 недостаточная область, 429 превышен лимит частоты запросов, 402 достигнута месячная квота.

Формат ответов

Важный момент перед написанием вашего клиента: коллекции заключены в объект data, а единичные ресурсы возвращаются плоско. Чтение response.data.data для единичного ресурса возвращает undefined.

Коллекция — упакованнаяjson
// 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": [ /* … */ ]
}

Ограничения пропускной способности

Ограничения гарантируют стабильное качество обслуживания для всех клиентов. Если вам нужно больше, свяжитесь с нами.

  • 100 запросов в минуту на API ключ
  • Всплеск допустимо до 200 запросов менее чем за 10с
  • Ответ 429 с заголовком Retry-After, указывающим задержку в секундах