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

Download 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).