Quickstart
Esempi end-to-end di richiesta firma in cinque linguaggi. Tutti seguono lo stesso schema: leggi un PDF da disco, codificalo in base64, invia il dossier, ricevi lo signerUrl e (opzionalmente) consegnalo al firmatario in autonomia.
cURL
PDF_B64=$(base64 -w 0 contratto.pdf)
curl -X POST https://api.ezysign.net/api/v1/dossiers \
-H "Content-Type: application/json" \
-H "X-API-Key: esf_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d "{
\"signer\": {
\"firstName\": \"Mario\",
\"lastName\": \"Rossi\",
\"email\": \"[email protected]\"
},
\"otpChannel\": \"email\",
\"document\": {
\"name\": \"Contratto.pdf\",
\"contentBase64\": \"$PDF_B64\"
},
\"callbackUrl\": \"https://miosito.it/webhooks/ezysign\"
}"Node.js (fetch nativo)
import { readFile } from "node:fs/promises";
const API = "https://api.ezysign.net/api/v1";
const API_KEY = process.env.EZYSIGN_API_KEY;
async function createDossier() {
const pdf = await readFile("./contratto.pdf");
const res = await fetch(`${API}/dossiers`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify({
signer: {
firstName: "Mario",
lastName: "Rossi",
email: "[email protected]",
},
otpChannel: "email",
document: {
name: "Contratto.pdf",
contentBase64: pdf.toString("base64"),
},
callbackUrl: "https://miosito.it/webhooks/ezysign",
metadata: { orderId: "ORD-42" },
}),
});
if (!res.ok) {
throw new Error(`EzySign ${res.status}: ${await res.text()}`);
}
const dossier = await res.json();
console.log("ID: ", dossier.id);
console.log("Stato: ", dossier.status);
console.log("Link: ", dossier.signerUrl);
}
createDossier().catch(console.error);Python (requests)
import base64, os, requests
API = "https://api.ezysign.net/api/v1"
API_KEY = os.environ["EZYSIGN_API_KEY"]
with open("contratto.pdf", "rb") as f:
pdf_b64 = base64.b64encode(f.read()).decode("ascii")
payload = {
"signer": {
"firstName": "Mario",
"lastName": "Rossi",
"email": "[email protected]",
},
"otpChannel": "email",
"document": {
"name": "Contratto.pdf",
"contentBase64": pdf_b64,
},
"callbackUrl": "https://miosito.it/webhooks/ezysign",
"metadata": {"orderId": "ORD-42"},
}
r = requests.post(
f"{API}/dossiers",
json=payload,
headers={"X-API-Key": API_KEY},
timeout=30,
)
r.raise_for_status()
dossier = r.json()
print("ID: ", dossier["id"])
print("Stato: ", dossier["status"])
print("Link: ", dossier["signerUrl"])PHP (Guzzle)
<?php
require 'vendor/autoload.php';
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.ezysign.net/api/v1/',
'headers' => ['X-API-Key' => getenv('EZYSIGN_API_KEY')],
]);
$pdfB64 = base64_encode(file_get_contents('contratto.pdf'));
$res = $client->post('dossiers', [
'json' => [
'signer' => [
'firstName' => 'Mario',
'lastName' => 'Rossi',
'email' => '[email protected]',
],
'otpChannel' => 'email',
'document' => [
'name' => 'Contratto.pdf',
'contentBase64' => $pdfB64,
],
'callbackUrl' => 'https://miosito.it/webhooks/ezysign',
],
]);
$dossier = json_decode($res->getBody(), true);
echo "ID: {$dossier['id']}\n";
echo "Stato: {$dossier['status']}\n";
echo "Link: {$dossier['signerUrl']}\n";Go
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
const apiBase = "https://api.ezysign.net/api/v1"
type signer struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
}
type document struct {
Name string `json:"name"`
ContentBase64 string `json:"contentBase64"`
}
type req struct {
Signer signer `json:"signer"`
OtpChannel string `json:"otpChannel"`
Document document `json:"document"`
CallbackURL string `json:"callbackUrl,omitempty"`
}
func main() {
pdf, err := os.ReadFile("contratto.pdf")
if err != nil { panic(err) }
payload, _ := json.Marshal(req{
Signer: signer{
FirstName: "Mario", LastName: "Rossi",
Email: "[email protected]",
},
OtpChannel: "email",
Document: document{
Name: "Contratto.pdf",
ContentBase64: base64.StdEncoding.EncodeToString(pdf),
},
CallbackURL: "https://miosito.it/webhooks/ezysign",
})
r, _ := http.NewRequest("POST", apiBase+"/dossiers", bytes.NewReader(payload))
r.Header.Set("Content-Type", "application/json")
r.Header.Set("X-API-Key", os.Getenv("EZYSIGN_API_KEY"))
res, err := http.DefaultClient.Do(r)
if err != nil { panic(err) }
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
if res.StatusCode >= 300 {
fmt.Printf("error %d: %s\n", res.StatusCode, body)
return
}
fmt.Println(string(body))
}Polling dello stato (alternativa ai webhook)
Se non puoi esporre un endpoint pubblico, puoi interrogare periodicamente il dossier per verificarne lo stato. Frequenza consigliata: una richiesta ogni 30-60 secondi finché lo stato non diventa terminale (signed, expired, cancelled).
# Esempio in cURL
curl -s https://api.ezysign.net/api/v1/dossiers/8c1e4f2a-... \
-H "X-API-Key: esf_..." | jq .statusDownload del PDF firmato
Quando il dossier passa in signed (via webhook o polling), il campo signedDocumentUrl contiene un URL pre-firmato valido per 2 ore. Scaricalo e archivialo nel tuo storage:
# Node.js
import { writeFile } from "node:fs/promises";
const r = await fetch(dossier.signedDocumentUrl);
const pdf = Buffer.from(await r.arrayBuffer());
await writeFile(`./signed/${dossier.id}.pdf`, pdf);Annullare un dossier
curl -X DELETE https://api.ezysign.net/api/v1/dossiers/8c1e4f2a-... \
-H "X-API-Key: esf_..."Risposta: { "ok": true }. Il link firma viene invalidato; un dossier già in stato signed non può essere annullato (403 Forbidden).