Ir al contenido principal
Certyneo
API pública v1

Integre la firma electrónica en su stack

Envíe sobres, haga seguimiento de firmas, reciba webhooks. API REST simple, OpenAPI 3.0, ejemplos curl/Node/Python — todo para conectar Certyneo con su HRIS, CRM o software empresarial en pocas horas.

Inicio rápido

Tres pasos: cree una clave API desde los parámetros, codifique su PDF en base64, envíe. La respuesta contiene el `signUrl` que puede compartir directamente con el destinatario.

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
// npm install @certyneo/sdk  (or call fetch directly)
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"])

Pruebe la API desde sus herramientas

La colección Postman y la ficha RapidAPI se generan desde la especificación OpenAPI que también documenta esta página. Los tres permanecen alineados con la API real, endpoint por endpoint, en lugar de divergir en el primer añadido.

Colección Postman

Las 22 solicitudes organizadas por dominio — sobres, documentos, modelos, sellos, webhooks — con un ejemplo de cuerpo y respuesta para cada una. Pegue su clave en la variable apiKey de la colección, luego lance GET /health: no requiere autenticación y confirma que su configuración es correcta antes de la primera llamada autenticada.

Ficha RapidAPI

El mismo catálogo de endpoints, probable directamente desde el navegador. El banco de pruebas espera dos encabezados distintos: la clave RapidAPI que la plataforma le asigna, y su clave Certyneo en Authorization — es la segunda la que autoriza realmente la llamada.

Sobres

Creación, envío, seguimiento de estado, cancelación. Un sobre puede contener múltiples documentos y múltiples firmantes (paralelo o secuencial).

Las webhooks

Todos los eventos de sobre y destinatario (`envelope.sent`, `recipient.signed`, `envelope.completed`…) entregados en la URL de su elección — lista completa en /developers/webhooks. HMAC SHA-256 en cada carga útil para verificar el origen.

Autenticación simple

Token bearer. Una clave por entorno (test / prod). Revocable instantáneamente. Límite 100 req/min/clave, burst de 200, 429 limpio con encabezado Retry-After.

Puntos finales disponibles

12 rutas que cubren el ciclo completo: sobres, documentos, webhooks, claves API. Todas las rutas aceptan un token bearer y devuelven 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/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

Plantillas de sobre

Una plantilla registra de una vez por todas el PDF, los roles y la ubicación de los campos de firma, luego se reutiliza en cada envío: pase su identificador en templateId en lugar de documentIds, y los campos posicionados se copian en el sobre creado.

  • Las plantillas se crean desde el panel de control (Plantillas → Nueva plantilla), donde deposita el documento y posiciona los campos con el ratón — este es el camino más simple. La API también permite crearlas mediante POST /api/v1/templates, proporcionando los documentos, los roles y los campos; tenga en cuenta que los campos se posicionan con coordenadas absolutas (página, x, y, ancho, alto), por lo que debe conocer la maquetación del PDF.
  • En una cuenta que aún no ha registrado ninguna plantilla, GET /api/v1/templates devuelve una lista vacía. Este es el comportamiento normal, no un defecto de autenticación.
  • La lista contiene solo las plantillas que pertenecen al usuario propietario de la clave API. Una plantilla creada por un colega no aparece, ni siquiera dentro de un espacio de trabajo compartido: genere la clave desde la cuenta que posee la plantilla.
  • templateId y documentIds se excluyen mutuamente: envíe uno u otro, nunca ambos ni ninguno.
  • Proporcione al menos tantos destinatarios SIGNER como roles de firmantes tenga la plantilla, de lo contrario la creación se rechaza con un 400. El campo signerCount devuelto por la lista indica el número esperado.
  • El nivel de firma de la plantilla se hereda por el sobre, a menos que la solicitud pase explícitamente 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.

Desde Power Automate o Zapier

La acción « Crear un sobre » de nuestros conectores sin código solo cubre la ruta por plantilla: el campo Plantilla es obligatorio. Para un documento ad hoc que cambia en cada ejecución, utilice la acción « Cargar un documento » seguida de una acción HTTP bruta hacia POST /api/v1/envelopes pasando el documentIds retornado.

Envío del documento: dos formas aceptadas

POST /api/v1/documents acepta el archivo de dos formas, a su elección. Los mismos controles se aplican en ambos casos: tipos permitidos, límite de 50 Mo, verificación de firma binaria y análisis antivirus.

  • En multipart/form-data, con una parte denominada file. Esta es la forma clásica, la de curl -F y de la mayoría de las bibliotecas.
  • En cuerpo bruto: los bytes del archivo constituyen el cuerpo de la solicitud, y el encabezado Content-Type proporciona su tipo (application/pdf por ejemplo). Útil desde una herramienta que transmite el contenido tal cual, sin envolver la solicitud — esto es lo que hace el conector Power Automate.
  • En cuerpo bruto, el nombre del archivo no tiene lugar en el cuerpo: indíquelo a través del encabezado X-File-Name o el parámetro ?fileName=. Sin él, el documento se nombra según su tipo.
  • Un tipo no soportado responde 415 nombrando las dos formas aceptadas, y un cuerpo anunciado como multipart pero ilegible responde 400. Ninguno de los dos es un error de servidor.
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.

Colocación de campos de firma

Sin plantilla, un sobre creado a partir de documentIds no tiene ningún campo preposicionado: el firmante recibe el documento sin ubicación para firmar. La matriz fields, transmitida en la misma llamada de creación, coloca cada campo al punto exacto — es el equivalente, en el lado de la API, de lo que una plantilla registra de una vez por todas.

  • Las coordenadas están en puntos PDF, origen en la esquina superior izquierda de la página y eje Y hacia abajo (una página A4 mide 595 × 842 puntos). x e y designan la esquina superior izquierda del campo, width y height su tamaño.
  • pageNumber comienza en 1, documentIndex comienza en 0. Un número de página más allá del documento no se rechaza en la creación: el campo se ignora en el momento de la firma y no aparece en ninguna parte — es lo primero que hay que verificar cuando falta un campo en la llamada.
  • recipientEmail debe corresponder a uno de los destinatarios de la misma llamada, sin distinción de mayúsculas y minúsculas. De lo contrario, la creación falla enumerando todas las líneas defectuosas, lo que evita corregirlas una por una.
  • fields y templateId se excluyen mutuamente: una plantilla ya lleva su propio diseño. Por lo tanto, la matriz fields solo se utiliza con documentIds.
  • Tipos aceptados: SIGNATURE, INITIALS, DATE_SIGNED, TEXT, CHECKBOX y RADIO_GROUP. required es verdadero por defecto; placeholder y dateFormat son opcionales, y options solo se retiene para RADIO_GROUP.
  • Un sobre acepta un máximo de 100 campos, 20 documentos y 50 destinatarios — los límites de su plan pudiendo ser inferiores.
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.

Anclas de texto: colocar un campo sin conocer las coordenadas

En lugar de coordenadas, un campo puede citar un texto impreso en el documento: anchorText lo localiza en el PDF y el servidor calcula la posición en la creación. Este es el modo a privilegiar cuando el documento se regenera en cada envío — combinación de correspondencia, generador de contratos — porque el diseño cambia mientras que la mención "Firma del cliente" permanece. anchorPlacement indica de qué lado del texto se coloca el campo (right por defecto, si no below, above o left) y anchorIndex elige la ocurrencia cuando el texto aparece varias veces.

x e y siguen siendo obligatorios incluso con un ancla: sirven como respaldo. Un ancla no encontrada no hace fallar la creación — el campo mantiene la posición literal que proporcionó, sin error ni advertencia en la respuesta. Indique por lo tanto un respaldo plausible en lugar de 0,0, y verifique el resultado en un primer envío.

Autenticación

Cada llamada lleva una clave API en el encabezado Authorization. Las claves se generan desde Configuración → Claves API y solo se muestran una vez.

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" } }
  • Formato: sk_live_… en producción, sk_test_… para la zona de pruebas. Encabezado: Authorization: Bearer <clé>.
  • Alcances: envelopes, documents, webhooks, seals — en lectura (:read) o escritura (:write). La escritura implica la lectura; el alcance * otorga todos los derechos.
  • Las claves sk_test_ crean recursos en el entorno de pruebas, excluidos de la cuota y la facturación.
  • Errores: 401 clave inválida, 403 alcance insuficiente, 429 límite de velocidad excedido, 402 cuota mensual alcanzada.

Forma de las respuestas

Un punto a conocer antes de escribir tu cliente: las colecciones se encapsulan en un objeto data, mientras que los recursos unitarios se devuelven planos. Leer response.data.data en un recurso unitario devuelve por lo tanto undefined.

Colección — encapsuladajson
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
  "data": [
    { "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
  ]
}
Recurso unitario — planojson
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* … */ ]
}

Límites de velocidad

Los límites garantizan una calidad de servicio estable para todos los clientes. Si necesita más, contáctenos.

  • 100 solicitudes por minuto por clave API
  • Burst tolerado hasta 200 solicitudes en menos de 10s
  • Respuesta 429 con encabezado Retry-After indicando el retraso en segundos