Vai al contenuto principale
Certyneo
API pubblica v1

Integra la firma elettronica nel tuo stack

Invia buste, traccia le firme, ricevi webhook. API REST semplice, OpenAPI 3.0, esempi curl/Node/Python — tutto per collegare Certyneo al tuo HRIS, CRM o software gestionale in poche ore.

Avvio rapido

Tre passaggi: crea una chiave API dalle impostazioni, codifica il tuo PDF in base64, invia. La risposta contiene l'`signUrl` che puoi condividere direttamente con il 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
// 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"])

Prova l'API dai tuoi strumenti

La raccolta Postman e la scheda RapidAPI sono generate dalla specifica OpenAPI che documenta anche questa pagina. I tre rimangono quindi allineati all'API reale, endpoint per endpoint, piuttosto che divergere al primo aggiunta.

Raccolta Postman

Le 25 richieste organizzate per dominio — buste, documenti, modelli, sigilli, webhook — con un esempio di corpo e risposta per ciascuna. Incollate la vostra chiave nella variabile apiKey della raccolta, quindi avviate GET /health: non richiede alcuna autenticazione e conferma che la vostra configurazione è corretta prima della prima chiamata autenticata.

Scheda RapidAPI

Lo stesso catalogo di endpoint, testabile direttamente dal browser. Il banco di prova richiede due intestazioni distinte: la chiave RapidAPI che la piattaforma vi assegna, e la vostra chiave Certyneo in Authorization — è la seconda che autorizza realmente la chiamata.

Buste

Creazione, invio, tracciamento dello stato, annullamento. Una busta può contenere più documenti e più firmatari (parallelo o sequenziale).

Webhook

Tutti gli eventi di busta e destinatario (`envelope.sent`, `recipient.signed`, `envelope.completed`…) consegnati all'URL di vostra scelta — elenco completo su /developers/webhooks. HMAC SHA-256 su ogni payload per verificare l'origine.

Autenticazione semplice

Bearer token. Una chiave per ambiente (test / prod). Revocabile istantaneamente. Limite 100 req/min/chiave, burst di 200, 429 pulito con header Retry-After.

Endpoint disponibili

I 52 percorsi pubblici: account, documenti, buste, modelli, webhook, sigilli elettronici, chiavi API e fatturazione. Tutti accettano un token Bearer e restituiscono JSON.

MetodoPercorsoDescrizione
GET/api/v1/account/meIdentità del chiamante autenticato (id, e-mail, piano) : test degli identificativi senza scope richiesto
GET/api/v1/sandboxContare i dati creati con le chiavi di test (eliminati automaticamente dopo 30 giorni)
DELETE/api/v1/sandboxEliminare tutti i dati creati con le chiavi di test, senza toccare i dati reali
POST/api/v1/documentsCaricare un PDF (multipart) : restituisce l'id del documento
GET/api/v1/documentsElencare i documenti
GET/api/v1/documents/{id}Leggere i metadati di un documento
DELETE/api/v1/documents/{id}Eliminare un documento
GET/api/v1/envelopesElencare le buste (filtrare con ?status= e ?limit=)
POST/api/v1/envelopesCreare una busta (stato DRAFT) : a partire da un templateId, o da documentIds con un array fields facoltativo
GET/api/v1/envelopes/{id}Leggere lo stato di una busta
PATCH/api/v1/envelopes/{id}Modificare una busta DRAFT
DELETE/api/v1/envelopes/{id}Eliminare una busta DRAFT (409 una volta inviata : annullarla invece)
POST/api/v1/envelopes/{id}/sendInviare una busta DRAFT : spedisce gli inviti
POST/api/v1/envelopes/{id}/voidAnnullare una busta inviata non ancora completamente firmata : i firmatari in sospeso vengono notificati
GET/api/v1/envelopes/{id}/audit-trailScaricare il PDF del giornale di prova eIDAS
POST/api/v1/envelopes/{id}/embed-urlRestituisce l'URL di firma di un destinatario, corredato da un'autorizzazione di framing per l'origine richiesta. Da chiamare al momento del clic: l'autorizzazione vale due ore.
GET/api/v1/audit-anchors/{root}Pubblico : metadati di un lotto di audit ancorato, o file di prova .ots con ?format=ots (senza autenticazione)
POST/api/v1/audit-anchors/verifyPubblico : verificare una voce di audit e la sua prova rispetto alla radice di Merkle ancorata (senza autenticazione, non rivela nulla)
GET/api/v1/envelopes/{id}/signed-documentScaricare il PDF firmato (una volta COMPLETED)
GET/api/v1/envelopes/bulkElencare i vostri invii in massa
POST/api/v1/envelopes/bulkInvio in massa: creare N buste a partire da un modello e da un CSV (solo Standard/Business)
GET/api/v1/envelopes/bulk/{id}Avanzamento di un invio in massa: contatori, errori per riga, buste create
GET/api/v1/templatesElencare i modelli di busta riutilizzabili
POST/api/v1/templatesCreare un modello (documenti, ruoli, campi posizionati)
GET/api/v1/workspacesElenca gli spazi di lavoro in cui puoi creare una busta
POST/api/v1/sepa-mandatesGenerare il PDF di un mandato SEPA e la sua busta DRAFT, campi già posizionati
POST/api/v1/payroll-adapters/normalizeNormalizzare un CSV di stipendio (Silae, Sage Paie, PayFit, Lucca) al formato dell'invio in massa
POST/api/v1/ag-coproprieteCreare una busta di assemblea generale di condominio (risoluzioni e millesimi)
POST/api/v1/ag-copropriete/{envelopeId}/votesRegistrare i voti di un condomino sulle risoluzioni dell'assemblea
POST/api/v1/ag-copropriete/{envelopeId}/tallyConteggiare l'assemblea: risultato per risoluzione, ponderato per i millesimi
GET/api/v1/videos/{videoId}Scaricare un video di identità conservato: diritto di accesso RGPD (art. 15)
GET/api/v1/webhooksElencare i webhook
POST/api/v1/webhooksRegistrare un webhook: restituisce il segreto di firma una sola volta
GET/api/v1/webhooks/{id}Leggere un abbonamento webhook
PATCH/api/v1/webhooks/{id}Modificare l'URL, gli eventi o lo stato attivo
DELETE/api/v1/webhooks/{id}Annullare l'iscrizione al webhook
POST/api/v1/sealsApporre un sigillo elettronico qualificato su un documento
GET/api/v1/seals/{id}Leggere lo stato di un sigillo
GET/api/v1/seals/{id}/certificateScaricare il certificato del sigillo
GET/api/v1/payslips/workspacesBuste paga: gli spazi per i quali questa chiave distribuisce, detenuti o delegati da un cliente (studi contabili, gestori di stipendi)
GET/api/v1/payslips/employeesBuste paga: il registro dei dipendenti, con quelli che hanno rifiutato la consegna elettronica (busta paga cartacea dovuta)
PUT/api/v1/payslips/employeesBuste paga: aggiungere o aggiornare dipendenti dal registro (non li rimuove mai)
POST/api/v1/payslipsConsegnare una busta paga (PDF) nella cassaforte di un dipendente: sigillata, conservata 50 anni, riprovazione senza duplicati
GET/api/v1/payslipsElencare le buste paga consegnate, con la loro prova di consultazione
GET/api/v1/keysElencare le chiavi API
POST/api/v1/keysCreare una chiave API: il segreto viene visualizzato una sola volta
PATCH/api/v1/keys/{id}Rinominare o revocare una chiave
DELETE/api/v1/keys/{id}Eliminare una chiave
GET/api/v1/billing/usageConsumo del periodo corrente e costo previsto
GET/api/v1/statusStato del servizio
GET/api/v1/healthControllo di integrità: database, coda d'attesa e archivio oggetti
GET/api/v1/openapiSpecifica OpenAPI leggibile dalla macchina

Modelli di busta

Un modello registra una volta per tutte il PDF, i ruoli e la posizione dei campi di firma, poi si riutilizza a ogni invio: passate il suo identificativo in templateId al posto di documentIds, e i campi posizionati vengono copiati sulla busta creata.

  • • I modelli si creano dal dashboard (Modelli → Nuovo modello), dove depositate il documento e posizionate i campi con il mouse — è il percorso più semplice. L'API permette anche di crearli con POST /api/v1/templates, fornendo i documenti, i ruoli e i campi; attenzione, i campi vi sono posizionati in coordinate assolute (pagina, x, y, larghezza, altezza), quindi bisogna conoscere il layout del PDF.
  • • Su un account che non ha ancora registrato alcun modello, GET /api/v1/templates restituisce un elenco vuoto. È il comportamento normale, non un difetto di autenticazione.
  • • L'elenco contiene solo i modelli appartenenti all'utente proprietario della chiave API. Un modello creato da un collega non vi figura, anche all'interno di uno spazio di lavoro condiviso: generate la chiave dall'account che possiede il modello.
  • • templateId e documentIds si escludono a vicenda: inviate l'uno o l'altro, mai entrambi né nessuno dei due.
  • • Fornite almeno tanti destinatari SIGNER quanti ruoli firmatari il modello conta, altrimenti la creazione viene rifiutata con 400. Il campo signerCount restituito dall'elenco indica il numero previsto.
  • • Il livello di firma del modello viene ereditato dalla busta, a meno che la richiesta non passi esplicitamente 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.

Da Power Automate o Zapier

L'azione « Crea una busta » dei nostri connettori no-code copre solo il percorso per modello: il campo Modello vi è obbligatorio. Per un documento ad hoc che cambia a ogni esecuzione, utilizzate l'azione « Carica un documento » poi un'azione HTTP grezza verso POST /api/v1/envelopes passando il documentIds restituito.

Invio del documento: due forme accettate

POST /api/v1/documents accetta il file in due modi, a scelta. Gli stessi controlli si applicano in entrambi i casi: tipi autorizzati, limite di 50 Mo, verifica della firma binaria e analisi antivirus.

  • • In multipart/form-data, con una parte denominata file. È la forma classica, quella di curl -F e della maggior parte delle librerie.
  • • In corpo grezzo: gli ottetti del file costituiscono il corpo della richiesta, e l'intestazione Content-Type ne fornisce il tipo (application/pdf per esempio). Utile da uno strumento che trasmette il contenuto così com'è, senza incapsulare la richiesta — è quello che fa il connettore Power Automate.
  • • In corpo grezzo, il nome del file non ha spazio nel corpo: indicatelo tramite l'intestazione X-File-Name o il parametro ?fileName=. Senza di esso, il documento viene denominato in base al suo tipo.
  • • Un tipo non supportato risponde 415 nominando le due forme accettate, e un corpo annunciato come multipart ma illeggibile risponde 400. Nessuno dei due è un errore del server.
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.

Posizionamento dei campi di firma

Senza modello, una busta creata a partire da documentIds non ha alcun campo pre-posizionato: il firmatario riceve il documento senza luogo dove firmare. L'array fields, trasmesso nella stessa chiamata di creazione, posiziona ogni campo al punto preciso — è l'equivalente, lato API, di ciò che un modello registra una volta per tutte.

  • • Le coordinate sono in punti PDF, origine in alto a sinistra della pagina e asse Y verso il basso (una pagina A4 misura 595 × 842 punti). x e y designano l'angolo superiore sinistro del campo, width e height la sua dimensione.
  • • pageNumber inizia da 1, documentIndex inizia da 0. Un numero di pagina al di là del documento non viene rifiutato alla creazione: il campo viene ignorato al momento della firma e non appare da nessuna parte — è la prima cosa da verificare quando un campo manca alla chiamata.
  • • recipientEmail deve corrispondere a uno dei destinatari della stessa chiamata, senza distinzione di maiuscole/minuscole. Altrimenti la creazione fallisce elencando tutte le righe difettose, il che evita di correggerle una per una.
  • • fields e templateId si escludono a vicenda: un modello porta già il suo proprio layout. L'array fields viene quindi utilizzato solo con documentIds.
  • • Tipi accettati: SIGNATURE, INITIALS, DATE_SIGNED, TEXT, CHECKBOX e RADIO_GROUP. required vale true per impostazione predefinita; placeholder e dateFormat sono facoltativi, e options viene considerato solo per RADIO_GROUP.
  • • Una busta accetta al massimo 100 campi, 20 documenti e 50 destinatari — i limiti del vostro piano potendo essere inferiori.
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.

Ancore testuali: posizionare un campo senza conoscere le coordinate

Piuttosto che coordinate, un campo può citare un testo stampato nel documento: anchorText lo individua nel PDF e il server calcola la posizione alla creazione. È la modalità da privilegiare quando il documento viene rigenerato ad ogni invio — stampa unione, generatore di contratti — perché il layout si muove mentre la menzione "Firma del cliente" rimane. anchorPlacement indica da quale lato del testo si posiziona il campo (right per impostazione predefinita, altrimenti below, above o left) e anchorIndex sceglie l'occorrenza quando il testo appare più volte.

x e y rimangono obbligatori anche con un'ancora: servono come ripiego. Un'ancora non trovata non fa fallire la creazione — il campo mantiene la posizione letterale che avete fornito, senza errore né avviso nella risposta. Indicate quindi un ripiego plausibile piuttosto che 0,0 e verificate il rendering su un primo invio.

Firma integrata: firma senza lasciare la tua applicazione

Il tuo cliente visualizza e firma il suo documento dalla tua stessa interfaccia. La cerimonia si apre in una finestra sopra la tua pagina e torna da te automaticamente al termine. Dal lato server, nulla di nuovo: crei la busta come al solito specificando redirectUrl, quindi affidi al SDK l'URL di firma del destinatario.

  • • Chiama Certyneo.sign() direttamente nel tuo gestore di clic e passa la tua chiamata server tramite getUrl. Un await prima di sign() esce dal gesto dell'utente: Safari e Firefox bloccano allora la finestra. L'SDK la apre immediatamente e la carica quando la tua promessa si risolve.
  • • redirectUrl è il canale di ritorno. Vi rinviamo il firmatario aggiungendo certyneo_status (completed o declined) e certyneo_envelope. Caricate lo stesso script in questa pagina di ritorno: è lui che avvisa la finestra del portale rimasta aperta dietro.
  • • In modalità integrata, i nostri propri inviti all'azione sono soppressi. Il vostro cliente non vede mai un invito a creare un account Certyneo.
  • • Se il navigatore rifiuta la finestra, l'SDK passa da solo al reindirizzamento a pagina intera. Il percorso è identico per il firmatario e la vostra pagina di ritorno riceve esattamente gli stessi parametri.
  • • Volontariamente non esiste un callback «l'utente ha abbandonato»: lo stato della finestra non è leggibile dalla vostra pagina, e un tale callback si attiverebbe erroneamente. Una firma che non arriva rimane in sospeso fino al 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'evento del navigatore non è una prova di firma

onCompleted è un segnale di visualizzazione, emesso da un navigatore, quindi falsificabile da chiunque dalla sua console. Non basculare mai un dossier in «firmato» su questa base: usatelo per aggiornare lo schermo, e affidate vi al webhook signed envelope.completed ricevuto dal vostro server per lo stato reale. È la trappola classica delle integrazioni di pagamento, e si riproduce identicamente qui.

Sviluppare sulla vostra postazione

Con una chiave di test, redirectUrl accetta http://localhost (e 127.0.0.1): montate tutto il percorso sulla vostra macchina prima di implementare. Una chiave reale lo rifiuta, affinché nessuna busta di produzione rimandi un vero firmatario verso una macchina che non è la sua.

Visualizza la cerimonia nella tua pagina

La modalità frame visualizza la firma all'interno della tua interfaccia, senza finestra. Richiede un passaggio in più rispetto alla modalità finestra: provare che il sito che la visualizzerà ti appartiene. Questo è ciò che impedisce a terzi di incorniciare le nostre cerimonie per ingannare i tuoi clienti.

  • • Dichiara il tuo sito in Impostazioni → Firma integrata, quindi pubblica il record DNS indicato. Un indirizzo esatto: un sottodominio o una porta diversa conta come un altro sito.
  • • Ad ogni firma, richiedi un URL fresco a POST /api/v1/envelopes/{id}/embed-url. Contiene l'autorizzazione di visualizzazione e vale due ore, il che corrisponde al momento del clic, non alla creazione della busta.
  • • Due percorsi non possono svolgersi in un frame: la firma qualificata, la cui verifica dell'identità rifiuta di essere incorniciata a sua volta, e il pagamento alla firma. In entrambi i casi il firmatario viene indirizzato verso una finestra completa.
  • • La menzione « Powered by Certyneo » scompare dal frame a partire dal piano Business. La modalità frame stessa è inclusa a partire dal piano 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.

Far firmare senza che nessuna e-mail parta

Un portale che visualizza la cerimonia presso di sé spesso non ha bisogno del nostro invito: arriva insieme al suo percorso e propone un account a un cliente che non lo vuole. Date il valore NONE al canale di notifica di un destinatario e non gli viene più inviato nulla — siete voi a condurlo alla sua firma.

  • • L'impostazione è per destinatario, non per busta: potete silenziare il cliente che ospitate e mantenere l'e-mail per un controfirmatario esterno, che non ha un portale.
  • • È riservato ai firmatari e agli approvatori, gli unici a cui avete un link da consegnare. Lo leggete nella risposta di creazione e invio, oppure richiedete un URL fresco al momento del clic a POST /api/v1/envelopes/{id}/embed-url.
  • • I solleciti automatici si fermano da soli per questo destinatario: non gli abbiamo mai scritto, non iniziamo con un sollecito. Gli altri destinatari continuano a essere sollecitati normalmente.
  • • Il certificato di firma lo menziona, destinatario per destinatario. Un fascicolo probatorio che tacesse su questo lascerebbe credere che questo firmatario ha ricevuto da noi un invito che non è mai esistito.
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.

Visualizza il nome del tuo cliente come mittente

Per impostazione predefinita, il firmatario legge il nome del titolare della chiave API. Se fai firmare i documenti di più aziende con una sola chiave, passa il campo sender alla creazione della busta: la pagina di firma e le e-mail di invito e di sollecito visualizzano quindi il nome dell'azienda interessata.

  • • Riservato al piano Business e superiori. Su un piano inferiore, la creazione viene rifiutata piuttosto che lasciare che il firmatario legga un nome diverso da quello previsto.
  • • Il nome è una visualizzazione: il certificato e l'audit trail mantengono il mittente effettivo, e le e-mail provengono sempre da Certyneo.
  • • L'indirizzo è facoltativo. Senza di esso, il firmatario vede solo il nome, mai l'indirizzo del titolare della chiave.
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"
  }
}

Autenticazione

Ogni chiamata porta una chiave API nell'intestazione Authorization. Le chiavi si generano da Impostazioni → Chiavi API e vengono visualizzate una sola volta.

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_… in produzione, sk_test_… per la sandbox. Intestazione: Authorization: Bearer <clé>.
  • • Ambiti: envelopes, documents, webhooks, seals — in lettura (:read) o scrittura (:write). La scrittura implica la lettura; l'ambito * concede tutti i diritti.
  • • Le chiavi sk_test_ creano risorse in sandbox, escluse dalla quota e dalla fatturazione. Nessuna email reale viene inviata al destinatario, a meno che il suo indirizzo non corrisponda all'email dell'account che invia — utile per testare l'intero flusso su te stesso.
  • • Errori: 401 chiave non valida, 403 ambito insufficiente, 429 limite di velocità superato, 402 quota mensile raggiunta.

Forma delle risposte

Un punto da sapere prima di scrivere il tuo client: le collezioni sono incapsulate in un oggetto data, mentre le risorse unitarie vengono restituite in forma piatta. Leggere response.data.data su una risorsa unitaria restituisce quindi undefined.

Collezione — incapsulatajson
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
  "data": [
    { "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
  ]
}
Risorsa unitaria — in forma piattajson
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* … */ ]
}

Limiti di velocità

I limiti garantiscono una qualità del servizio stabile per tutti i clienti. Se hai bisogno di più, contattaci.

  • • 100 richieste per minuto per chiave API
  • • Burst tollerato fino a 200 richieste in meno di 10s
  • • Risposta 429 con header Retry-After che indica il ritardo in secondi

Approfondisci