Aller au contenu principal
Certyneo
API publique v1

Intégrez la signature électronique dans votre stack

Envoyez des enveloppes, suivez les signatures, recevez des webhooks. API REST simple, OpenAPI 3.0, exemples curl/Node/Python — tout pour brancher Certyneo sur votre HRIS, CRM ou logiciel métier en quelques heures.

Démarrage rapide

Trois étapes : créez une clé API depuis les paramètres, encodez votre PDF en base64, envoyez. La réponse contient le `signUrl` que vous pouvez partager directement avec le destinataire.

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"])

Essayer l'API depuis vos outils

La collection Postman et la fiche RapidAPI sont générées depuis la spécification OpenAPI qui documente aussi cette page. Les trois restent donc alignées sur l'API réelle, endpoint par endpoint, plutôt que de diverger au premier ajout.

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.

Enveloppes

Création, envoi, suivi d'état, annulation. Une enveloppe peut contenir plusieurs documents et plusieurs signataires (parallèle ou séquentiel).

Webhooks

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.

Authentification simple

Bearer token. Une clé par environnement (test / prod). Révocable instantanément. Limite 100 req/min/clé, burst de 200, 429 propre avec en-tête Retry-After.

Endpoints disponibles

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

MéthodeCheminDescription
GET/api/v1/account/meIdentité de l'appelant authentifié (id, e-mail, forfait) : test d'identifiants sans scope requis
GET/api/v1/sandboxCompter les données créées avec les clés de test (supprimées automatiquement au bout de 30 jours)
DELETE/api/v1/sandboxSupprimer toutes les données créées avec les clés de test, sans toucher aux données réelles
POST/api/v1/documentsTéléverser un PDF (multipart) : renvoie l'id du document
GET/api/v1/documentsLister les documents
GET/api/v1/documents/{id}Lire les métadonnées d'un document
DELETE/api/v1/documents/{id}Supprimer un document
GET/api/v1/envelopesLister les enveloppes (filtrer avec ?status= et ?limit=)
POST/api/v1/envelopesCréer une enveloppe (statut DRAFT) : à partir d'un templateId, ou de documentIds avec un tableau fields facultatif
GET/api/v1/envelopes/{id}Lire l'état d'une enveloppe
PATCH/api/v1/envelopes/{id}Modifier une enveloppe DRAFT
DELETE/api/v1/envelopes/{id}Supprimer une enveloppe DRAFT (409 une fois envoyée : l'annuler à la place)
POST/api/v1/envelopes/{id}/sendEnvoyer une enveloppe DRAFT : expédie les invitations
POST/api/v1/envelopes/{id}/voidAnnuler une enveloppe envoyée pas encore entièrement signée : les signataires en attente sont prévenus
GET/api/v1/envelopes/{id}/audit-trailTélécharger le PDF du journal de preuve eIDAS
POST/api/v1/envelopes/{id}/embed-urlRend l'URL de signature d'un destinataire, assortie d'une autorisation d'encadrement pour l'origine demandée. À appeler au moment du clic : l'autorisation vaut deux heures.
GET/api/v1/audit-anchors/{root}Public : métadonnées d'un lot d'audit ancré, ou fichier de preuve .ots avec ?format=ots (sans authentification)
POST/api/v1/audit-anchors/verifyPublic : vérifier une entrée d'audit et sa preuve face à la racine de Merkle ancrée (sans authentification, ne révèle rien)
GET/api/v1/envelopes/{id}/signed-documentTélécharger le PDF signé (une fois COMPLETED)
GET/api/v1/envelopes/bulkLister vos envois en masse
POST/api/v1/envelopes/bulkEnvoi en masse : créer N enveloppes à partir d'un modèle et d'un CSV (Standard/Business uniquement)
GET/api/v1/envelopes/bulk/{id}Avancement d'un envoi en masse : compteurs, échecs par ligne, enveloppes créées
GET/api/v1/templatesLister les modèles d'enveloppe réutilisables
POST/api/v1/templatesCréer un modèle (documents, rôles, champs positionnés)
GET/api/v1/workspacesLister les espaces de travail dans lesquels vous pouvez créer une enveloppe
POST/api/v1/sepa-mandatesGénérer le PDF d'un mandat SEPA et son enveloppe DRAFT, champs déjà placés
POST/api/v1/payroll-adapters/normalizeNormaliser un CSV de paie (Silae, Sage Paie, PayFit, Lucca) au format de l'envoi en masse
POST/api/v1/ag-coproprieteCréer une enveloppe d'assemblée générale de copropriété (résolutions et tantièmes)
POST/api/v1/ag-copropriete/{envelopeId}/votesEnregistrer les votes d'un copropriétaire sur les résolutions de l'assemblée
POST/api/v1/ag-copropriete/{envelopeId}/tallyDépouiller l'assemblée : résultat par résolution, pondéré par les tantièmes
GET/api/v1/videos/{videoId}Télécharger une vidéo d'identité conservée : droit d'accès RGPD (art. 15)
GET/api/v1/webhooksLister les webhooks
POST/api/v1/webhooksEnregistrer un webhook : renvoie le secret de signature une seule fois
GET/api/v1/webhooks/{id}Lire un abonnement webhook
PATCH/api/v1/webhooks/{id}Modifier l'URL, les événements ou l'état actif
DELETE/api/v1/webhooks/{id}Désinscrire le webhook
POST/api/v1/sealsApposer un cachet électronique qualifié sur un document
GET/api/v1/seals/{id}Lire le statut d'un cachet
GET/api/v1/seals/{id}/certificateTélécharger le certificat du cachet
GET/api/v1/payslips/workspacesBulletins de paie : les espaces pour lesquels cette clé distribue, détenus ou délégués par un client (cabinets comptables, gestionnaires de paie)
GET/api/v1/payslips/employeesBulletins de paie : le registre des salariés, avec ceux qui ont refusé la remise électronique (bulletin papier dû)
PUT/api/v1/payslips/employeesBulletins de paie : ajouter ou mettre à jour des salariés du registre (n'en retire jamais)
POST/api/v1/payslipsRemettre un bulletin de paie (PDF) dans le coffre d'un salarié : scellé, conservé 50 ans, réessai sans doublon
GET/api/v1/payslipsLister les bulletins remis, avec leur preuve de consultation
GET/api/v1/keysLister les clés d'API
POST/api/v1/keysCréer une clé d'API : le secret n'est affiché qu'une fois
PATCH/api/v1/keys/{id}Renommer ou révoquer une clé
DELETE/api/v1/keys/{id}Supprimer une clé
GET/api/v1/billing/usageConsommation de la période en cours et coût projeté
GET/api/v1/statusÉtat du service
GET/api/v1/healthContrôle de santé : base de données, file d'attente et stockage objet
GET/api/v1/openapiSpécification OpenAPI lisible par machine

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é.

Envoi du document : deux formes acceptées

POST /api/v1/documents accepte le fichier de deux façons, au choix. Les mêmes contrôles s'appliquent dans les deux cas : types autorisés, plafond de 50 Mo, vérification de la signature binaire et analyse antivirus.

  • • En multipart/form-data, avec une partie nommée file. C'est la forme classique, celle de curl -F et de la plupart des bibliothèques.
  • • En corps brut : les octets du fichier constituent le corps de la requête, et l'en-tête Content-Type en donne le type (application/pdf par exemple). Utile depuis un outil qui transmet le contenu tel quel, sans envelopper la requête — c'est ce que fait le connecteur Power Automate.
  • • En corps brut, le nom du fichier n'a pas de place dans le corps : indiquez-le via l'en-tête X-File-Name ou le paramètre ?fileName=. Sans lui, le document est nommé d'après son type.
  • • Un type non pris en charge répond 415 en nommant les deux formes acceptées, et un corps annoncé comme multipart mais illisible répond 400. Ni l'un ni l'autre n'est une erreur serveur.
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.

Placement des champs de signature

Sans modèle, une enveloppe créée à partir de documentIds n'a aucun champ pré-positionné : le signataire reçoit le document sans emplacement où signer. Le tableau fields, transmis dans le même appel de création, place chaque champ au point près — c'est l'équivalent, côté API, de ce qu'un modèle enregistre une fois pour toutes.

  • • Les coordonnées sont en points PDF, origine en haut à gauche de la page et axe Y vers le bas (une page A4 mesure 595 × 842 points). x et y désignent le coin supérieur gauche du champ, width et height sa taille.
  • • pageNumber commence à 1, documentIndex commence à 0. Un numéro de page au-delà du document n'est pas rejeté à la création : le champ est ignoré au moment de la signature et n'apparaît nulle part — c'est la première chose à vérifier lorsqu'un champ manque à l'appel.
  • • recipientEmail doit correspondre à l'un des destinataires du même appel, sans distinction de casse. Sinon la création échoue en listant toutes les lignes fautives, ce qui évite de les corriger une par une.
  • • fields et templateId s'excluent mutuellement : un modèle porte déjà sa propre mise en page. Le tableau fields ne s'utilise donc qu'avec documentIds.
  • • Types acceptés : SIGNATURE, INITIALS, DATE_SIGNED, TEXT, CHECKBOX et RADIO_GROUP. required vaut true par défaut ; placeholder et dateFormat sont facultatifs, et options n'est retenu que pour RADIO_GROUP.
  • • Une enveloppe accepte au maximum 100 champs, 20 documents et 50 destinataires — les limites de votre plan pouvant être inférieures.
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.

Ancres textuelles : placer un champ sans connaître les coordonnées

Plutôt que des coordonnées, un champ peut citer un texte imprimé dans le document : anchorText le repère dans le PDF et le serveur calcule la position à la création. C'est le mode à privilégier lorsque le document est régénéré à chaque envoi — publipostage, générateur de contrats — car la mise en page bouge tandis que la mention « Signature du client » reste. anchorPlacement indique de quel côté du texte se pose le champ (right par défaut, sinon below, above ou left) et anchorIndex choisit l'occurrence lorsque le texte apparaît plusieurs fois.

x et y restent obligatoires même avec une ancre : ils servent de repli. Une ancre introuvable ne fait pas échouer la création — le champ garde la position littérale que vous avez fournie, sans erreur ni avertissement dans la réponse. Indiquez donc un repli plausible plutôt que 0,0, et vérifiez le rendu sur un premier envoi.

Signature intégrée : signer sans quitter votre application

Votre client consulte et signe son document depuis votre propre interface. La cérémonie s'ouvre dans une fenêtre au-dessus de votre page, et il revient chez vous automatiquement une fois terminé. Côté serveur, rien de nouveau : vous créez l'enveloppe comme d'habitude en renseignant redirectUrl, puis vous confiez au SDK l'URL de signature du destinataire.

  • • Appelez Certyneo.sign() directement dans votre gestionnaire de clic, et passez votre appel serveur par getUrl. Un await avant sign() sort du geste utilisateur : Safari et Firefox bloquent alors la fenêtre. Le SDK l'ouvre sur-le-champ et la charge quand votre promesse se résout.
  • • redirectUrl est le canal de retour. Nous y renvoyons le signataire en ajoutant certyneo_status (completed ou declined) et certyneo_envelope. Chargez le même script sur cette page de retour : c'est lui qui prévient la fenêtre du portail restée ouverte derrière.
  • • En mode intégré, nos propres appels à l'action sont supprimés. Votre client ne voit jamais d'invitation à créer un compte Certyneo.
  • • Si le navigateur refuse la fenêtre, le SDK bascule tout seul en redirection pleine page. Le parcours est identique pour le signataire et votre page de retour reçoit exactement les mêmes paramètres.
  • • Il n'existe volontairement pas de rappel « l'utilisateur a abandonné » : l'état de la fenêtre n'est pas lisible depuis votre page, et un tel rappel se déclencherait à tort. Une signature qui n'arrive pas reste en attente jusqu'au webhook.
JavaScriptjavascript
// 1. Your server creates and sends the envelope, then hands the browser the
//    recipient's signing URL. Nothing new on the API side:
//      POST /api/v1/envelopes        { …, "redirectUrl": "https://your-app.com/quote/42/signed" }
//      POST /api/v1/envelopes/{id}/send   → recipients[].accessToken
//      signingUrl = "https://certyneo.com/sign/" + accessToken

// 2. On your quote page — load the SDK once:
//      <script src="https://certyneo.com/embed.js"></script>

document.querySelector("#sign").addEventListener("click", () => {
  Certyneo.sign({
    // Called AFTER the window is already open, so no popup blocker ever
    // sees a delay between the click and window.open.
    getUrl: () =>
      fetch("/api/quote/42/signature", { method: "POST" })
        .then((r) => r.json())
        .then((d) => d.signingUrl),

    onCompleted: (e) => showConfirmation(e.envelopeId),
    onDeclined: () => showDeclined(),
    onError: (e) => showError(e),
  });
});

// 3. On https://your-app.com/quote/42/signed — your return page — load the
//    same script and render a short confirmation. It relays the outcome to
//    the portal window behind it, then closes itself.

L'événement du navigateur n'est pas une preuve de signature

onCompleted est un signal d'affichage, émis par un navigateur, donc falsifiable par n'importe qui depuis sa console. Ne faites jamais basculer un dossier en « signé » sur cette base : servez-vous-en pour rafraîchir l'écran, et fiez-vous au webhook signé envelope.completed reçu par votre serveur pour l'état réel. C'est le piège classique des intégrations de paiement, et il se reproduit à l'identique ici.

Développer sur votre poste

Avec une clé de test, redirectUrl accepte http://localhost (et 127.0.0.1) : vous montez tout le parcours sur votre machine avant de déployer. Une clé réelle le refuse, pour qu'aucune enveloppe de production ne renvoie un vrai signataire vers une machine qui n'est pas la sienne.

Afficher la cérémonie dans votre page

Le mode cadre affiche la signature à l'intérieur de votre interface, sans fenêtre. Il demande une étape de plus que le mode fenêtre : prouver que le site qui l'affichera vous appartient. C'est ce qui empêche un tiers d'encadrer nos cérémonies pour tromper vos clients.

  • • Déclarez votre site dans Réglages → Signature intégrée, puis publiez l'enregistrement DNS indiqué. Une adresse exacte : un sous-domaine ou un port différent compte comme un autre site.
  • • À chaque signature, demandez une URL fraîche à POST /api/v1/envelopes/{id}/embed-url. Elle porte l'autorisation d'affichage et vaut deux heures, ce qui correspond au moment du clic, pas à la création de l'enveloppe.
  • • Deux parcours ne peuvent pas se dérouler dans un cadre : la signature qualifiée, dont la vérification d'identité refuse d'être encadrée à son tour, et le paiement à la signature. Dans les deux cas le signataire est orienté vers une fenêtre complète.
  • • La mention « Propulsé par Certyneo » disparaît du cadre à partir du plan Business. Le mode cadre lui-même est inclus dès le plan Standard.
JavaScriptjavascript
// Mode cadre : la cérémonie s'affiche DANS votre page.
//
// 1. Déclarez votre site une fois pour toutes dans Réglages → Signature
//    intégrée, et publiez l'enregistrement DNS qu'on vous y donne.
//
// 2. À chaque signature, votre serveur demande une URL fraîche. Elle porte
//    l'autorisation qui permet au navigateur d'afficher la cérémonie chez
//    vous, et vaut deux heures :
//
//      POST /api/v1/envelopes/{id}/embed-url
//      { "origin": "https://portail.exemple.fr" }
//      -> { "signingUrl": "…", "expiresAt": "…" }

Certyneo.sign({
  mode: "iframe",
  el: "#zone-signature",        // sélecteur ou nœud de votre page

  getUrl: () =>
    fetch("/api/devis/42/signature", { method: "POST" })
      .then((r) => r.json())
      .then((d) => d.signingUrl),

  onCompleted: (e) => afficherConfirmation(e.envelopeId),
  onDeclined: () => afficherRefus(),
});

// Le cadre parle à votre page par postMessage — pas besoin de page de retour,
// contrairement au mode fenêtre.

Faire signer sans qu'aucun e-mail ne parte

Un portail qui affiche la cérémonie chez lui n'a souvent pas besoin de notre invitation : elle arrive à côté de son propre parcours et propose un compte à un client qui n'en veut pas. Donnez la valeur NONE au canal de notification d'un destinataire et plus rien ne lui est envoyé — c'est vous qui l'emmenez sur sa signature.

  • • Le réglage est par destinataire, pas par enveloppe : vous pouvez faire taire le client que vous hébergez et garder l'e-mail pour un contresignataire externe, qui lui n'a pas de portail.
  • • Il est réservé aux signataires et aux approbateurs, les seuls à qui vous avez un lien à remettre. Vous le lisez dans la réponse de la création et de l'envoi, ou vous demandez une URL fraîche au moment du clic à POST /api/v1/envelopes/{id}/embed-url.
  • • Les relances automatiques s'arrêtent d'elles-mêmes pour ce destinataire : nous ne lui avons jamais écrit, nous ne commençons pas par une relance. Les autres destinataires continuent d'être relancés normalement.
  • • Le certificat de signature le mentionne, destinataire par destinataire. Un dossier de preuve qui se tairait là-dessus laisserait croire que ce signataire a reçu de nous une invitation qui n'a jamais existé.
JSONjson
POST /api/v1/envelopes

{
  "subject": "Mandat de gestion",
  "documentIds": ["doc_8f2c1a4b9e7d"],
  "recipients": [
    {
      "email": "client@exemple.fr",
      "name": "Camille Client",
      "role": "SIGNER",
      "notificationChannel": "NONE"
    },
    {
      "email": "notaire@exemple.fr",
      "name": "Maître Notaire",
      "role": "SIGNER"
    }
  ]
}

// La réponse rend le lien de chaque signataire :
//
//   "recipients": [
//     { "email": "client@exemple.fr",  "accessToken": "…",
//       "notificationChannel": "NONE"  },
//     { "email": "notaire@exemple.fr", "accessToken": "…",
//       "notificationChannel": "EMAIL" }
//   ]
//
// Le client ne reçoit rien : vous l'emmenez sur sa signature depuis votre
// portail. Le notaire, lui, reçoit son invitation comme d'habitude.

Afficher le nom de votre client comme expéditeur

Par défaut, le signataire lit le nom du titulaire de la clé API. Si vous faites signer les documents de plusieurs entreprises avec une seule clé, passez le champ sender à la création de l'enveloppe : la page de signature et les e-mails d'invitation et de relance affichent alors le nom de l'entreprise concernée.

  • • Réservé au plan Business et au-delà. Sur un plan inférieur, la création est refusée plutôt que de laisser le signataire lire un autre nom que celui attendu.
  • • Le nom est un affichage : le certificat et la piste d'audit gardent l'expéditeur réel, et les e-mails partent toujours de Certyneo.
  • • L'adresse est facultative. Sans elle, le signataire ne voit que le nom, jamais l'adresse du titulaire de la clé.
JSONjson
POST /api/v1/envelopes

{
  "subject": "Devis 2026-118",
  "documentIds": ["doc_8f2c1a4b9e7d"],
  "recipients": [
    { "email": "client@exemple.fr", "name": "Camille Client", "role": "SIGNER" }
  ],
  "sender": {
    "name": "1.2.3. Panneaux Solaires",
    "email": "devis@123-panneaux-solaires.fr"
  }
}

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.
  • • Les clés sk_test_ créent des ressources en bac à sable, exclues du quota et de la facturation. Aucun e-mail réel n'est envoyé au destinataire, sauf si son adresse est celle du compte qui envoie l'enveloppe — utile pour tester le parcours de bout en bout sur soi-même.
  • • 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" }
  ]
}
Ressource unitaire — à platjson
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* … */ ]
}

Limites de débit

Les limites garantissent une qualité de service stable pour tous les clients. Si vous avez besoin de plus, contactez-nous.

  • • 100 requêtes par minute par clé API
  • • Burst toléré jusqu'à 200 requêtes en moins de 10s
  • • Réponse 429 avec en-tête Retry-After indiquant le délai en secondes

Aller plus loin