CertiSource API Reference Versione 2.0 · Marzo 2026

Atti Service API

API REST per richiedere, monitorare e scaricare documenti camerali italiani: visure ordinarie e storiche, atti ottici repertoriati, statuti societari.

BASE URL https://certisource.it/atti-service.php

Come funziona

Il flusso è asincrono: la richiesta viene accettata immediatamente, il documento è pronto dopo alcuni secondi/minuti. L'applicazione fa polling su ?action=stato finché completed: true.

1

Autenticati

Usa il tuo Personal API Token (cs_pat_...) oppure ottieni un JWT con il flusso Client Credentials. Ogni metodo è descritto nella sezione Autenticazione.

2

Richiedi il documento

POST ?action=richiesta con P.IVA e tipo documento. Ricevi subito l'id della richiesta e il costo addebitato. Consulta prima ?action=prezzi per il catalogo prezzi (no auth).

3

Fai polling sullo stato

GET ?action=stato&id=N ogni 15 secondi finché "completed": true.

4

Scarica il PDF

Usa il download_url restituito da stato, oppure chiama direttamente GET ?action=scarica&id=N.


Autenticazione

Tre metodi disponibili. Se hai un account CertiSource con credito, usa il Personal API Token: è il più semplice e i documenti vengono addebitati direttamente sul tuo saldo.

Personal API Token (PAT)

Authorization: Bearer cs_pat_••••••

Genera il token dal tuo profilo CertiSource. I costi scalano dal tuo credito account (voucher-first). Nessun JWT da rinnovare. La scadenza è opzionale (nessuna, 30gg, 90gg, 1 anno) — impostata al momento della creazione.

Come generare il token →

B2B Client Credentials

Authorization: Bearer <JWT>

Per app senza un utente CertiSource. Ottieni client_id + client_secret, chiama POST ?action=token, ricevi un JWT valido 24h.

Flusso Client Credentials →

💡 Il PAT ha scadenza opzionale (nessuna, 30gg, 90gg o 1 anno) — impostata al momento della creazione e visibile in expires_at. Il JWT B2B scade dopo token_ttl_hours ore (default 24h) e va rinnovato automaticamente.

Costi & Addebiti

Ogni richiesta andata a buon fine genera un addebito immediato sul credito dell'account. Il credito promozionale (voucher) viene consumato prima di quello reale.

DocumentoProviderCostoTempo medio
Visura Camerale Ordinaria
Sede legale + unità locali, REA, cariche, soci/quote, ATECO, PEC, oggetto sociale, capitale sociale
visurecamerali.openapi.it € 4,95 30–120 s
Visura Storica
Tutta la storia societaria dalla fondazione
visurecamerali.openapi.it € 5,90 60–180 s
Atto Ottico CCIAA
Documento repertoriato depositato al RI (richiede numero protocollo)
docuengine.openapi.com € 5,50 10–60 s
Statuto Societario
Statuto aggiornato depositato al Registro Imprese
docuengine.openapi.com € 5,50 10–60 s
💡 CertiSource gestisce automaticamente la cache lato server: se richiedi la stessa P.IVA per una visura (camerale o storica) entro 10 giorni dall'ultima acquisizione, il documento viene restituito subito senza nuovi costi OpenAPI. Aggiungi "force": true al body per bypassare la cache.

In caso di credito insufficiente l'API risponde con HTTP 402 e non effettua alcun addebito né richiesta al provider.

🤖 Per leggere il catalogo prezzi in modo programmatico usa GET ?action=prezzi (no auth, JSON strutturato con breakdown costi, durate cache, campi obbligatori e features per tipo documento).

Caching automatico

CertiSource memorizza ogni documento acquisito e lo riusa automaticamente nelle richieste successive per la stessa P.IVA. Il documento viene consegnato immediatamente al prezzo pieno: il client paga sempre, CertiSource non sostiene il costo del provider.

Tipo documentoDurata cacheCross-typePrezzo per il client
Visura Camerale Ordinaria240 ore (10 giorni)✅ Accetta anche Visura Storica in cachePrezzo pieno (€ 4,95)
Visura Storica240 ore (10 giorni)— solo tipo esattoPrezzo pieno (€ 5,90)
Atto Ottico / Statuto24 ore (stesso protocollo)Prezzo pieno
💡 Cache cross-type visure: se CertiSource ha già acquisito una visura_storica per una P.IVA, una successiva richiesta di visura_camerale per la stessa P.IVA (entro 10 giorni) viene servita dalla cache. La risposta include il campo cached_source_type per indicare il tipo del documento servito.
Risposta con cached=true
{
  "success":            true,
  "id":                 43,
  "cached":             true,
  "cached_from":        38,   // id richiesta originale
  "cached_source_type": "visura_storica",  // tipo del doc servito (può differire dal richiesto)
  "status":             "completed",
  "cost":               4.95,  // prezzo pieno del tipo richiesto
  "download_url":       "?action=scarica&id=43",
  "data_url":           "?action=dati&id=43",
  "message":            "Documento disponibile (originale del 2026-03-01 10:22:00) — servita visura_storica in cache."
}

Per forzare un nuovo acquisto (es. dopo una modifica societaria urgente) aggiungi "force": true nel body della richiesta.


GET ?action=prezzi

Catalogo prezzi machine-readable con breakdown dettagliato per ogni tipo di documento: costo totale, voci di costo, durata cache, tempi di consegna, campi obbligatori e features disponibili. Non richiede autenticazione.

💡 Usa questo endpoint per mostrare i prezzi nella tua UI prima che l'utente confermi l'acquisto, oppure per costruire dinamicamente i costi stimati nella tua integrazione.
cURL
curl "https://certisource.it/atti-service.php?action=prezzi"
Risposta
{
  "success":     true,
  "currency":    "EUR",
  "computed_at": "2026-02-27T14:00:00+01:00",
  "billing": {
    "model":                   "prepaid_credit",
    "voucher_first":           true,
    "insufficient_credit_http": 402,
    "note":                    "Il credito voucher viene consumato prima del credito reale. HTTP 402 se credito insufficiente."
  },
  "catalog": [
    {
      "type":        "visura_camerale",
      "name":        "Visura Camerale Ordinaria",
      "description": "Dati attuali: ragione sociale, sede legale, unità locali, REA, cariche con anagrafica completa, soci/quote, ATECO, PEC, oggetto sociale, capitale sociale, poteri di rappresentanza. Include JSON strutturato via ?action=dati.",
      "provider":    "visurecamerali.openapi.it",
      "cost": {
        "total": 4.95,
        "breakdown": [
          { "item": "documento CCIAA", "amount": 4.95 }
        ]
      },
      "cache_hours":  240,
      "cache_note":   "Documento riusato da cache (10 giorni) — costo pieno addebitato al cliente",
      "async":        true,
      "avg_delivery_seconds": { "min": 30, "max": 120 },
      "required_fields": ["vat"],
      "optional_fields": ["webhook_url", "webhook_secret", "force"],
      "features": {
        "structured_data": true,   // JSON via ?action=dati
        "webhook":         true,   // notifica push al completamento
        "caching":         true    // riuso automatico
      }
    },
    {
      "type":        "visura_storica",
      "description": "Tutti i dati della visura camerale + storia completa degli eventi societari dalla fondazione: nomine, cessioni quote, variazioni capitale, depositi bilancio, procedure concorsuali, fusioni. Include JSON strutturato con campo eventi[] via ?action=dati.",
      "cost": { "total": 5.90, "breakdown": [{ "item": "documento CCIAA storico", "amount": 5.90 }] },
      "cache_hours": 240, "required_fields": ["vat"],  "features": { "structured_data": true, "eventi": true, "webhook": true, "caching": true }
    },
    {
      "type":   "atto_ottico",
      "cost": {
        "total": 5.50,
        "breakdown": [
          { "item": "ricerca protocollo", "amount": 0.60 },
          { "item": "documento ottico",   "amount": 4.90 }
        ]
      },
      "cache_hours": 24,  "required_fields": ["vat", "protocol"],  "features": { "structured_data": false, "webhook": true, "caching": true }
    },
    {
      "type":   "statuto",
      "cost": { "total": 5.50, "breakdown": [{ "item": "ricerca protocollo", "amount": 0.60 }, { "item": "documento statuto", "amount": 4.90 }] },
      "cache_hours": 24,  "required_fields": ["vat", "protocol"],  "features": { "structured_data": false, "webhook": true, "caching": true }
    }
  ]
}

Schema risposta — campo catalog[n]

CampoTipoDescrizione
typestringIdentificatore da usare nel campo type di action=richiesta
cost.totalfloatCosto totale in EUR addebitato al completamento
cost.breakdownarrayVoci di costo singole (utile per atto_ottico: ricerca + documento)
cache_hoursintFinestra di cache in ore (visure: 240h = 10 giorni). Il documento viene riutilizzato automaticamente; il prezzo è comunque addebitato al cliente.
avg_delivery_secondsobjectmin e max in secondi per il completamento
required_fieldsstring[]Campi obbligatori nel body di action=richiesta
optional_fieldsstring[]Campi opzionali accettati
features.structured_databoolSe true: JSON strutturato disponibile via ?action=dati
features.webhookboolSe true: supporta notifica push webhook al completamento
features.cachingboolSe true: riuso automatico del documento entro la finestra cache
🔄 Cache e prezzo: se nella risposta catalog[n].cache_hours > 0, la seconda richiesta per la stessa vat + type entro quella finestra restituisce il documento già acquisito ("cached": true). Il prezzo è comunque addebitato per intero — CertiSource sostiene costo zero sull'OpenAPI, il cliente paga il prezzo standard. Usa "force": true nel body per forzare un nuovo acquisto dal registro CCIAA.

GET ?action=info

Catalogo pubblico del servizio con tipi di documento, costi e metodi di autenticazione. Non richiede autenticazione.

cURL
curl "https://certisource.it/atti-service.php?action=info"
Risposta
{
  "success": true,
  "service": "CertiSource Atti Service",
  "version": "1.2",
  "documents": [
    { "type": "visura_camerale", "cost_total": 4.95 },
    { "type": "visura_storica",  "cost_total": 5.90 },
    { "type": "atto_ottico",     "cost_total": 5.50 },
    { "type": "statuto",         "cost_total": 5.50 }
  ],
  "price_catalog": "https://certisource.it/atti-service.php?action=prezzi"
}

Per un catalogo prezzi strutturato e machine-readable usa ?action=prezzi.


POST ?action=token

Endpoint OAuth2 Client Credentials. Emette un JWT Bearer valido per la durata configurata. Solo per integrazione B2B senza utente CertiSource.

ℹ️ Se usi un Personal API Token (cs_pat_...) non hai bisogno di questo endpoint.

Parametri body (JSON)

CampoTipoNote
client_idstringobbligatorio UUID assegnato da CertiSource
client_secretstringobbligatorio Segreto (64 hex) — non recuperabile
cURL
curl -X POST "https://certisource.it/atti-service.php?action=token" \
     -H "Content-Type: application/json" \
     -d '{"client_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
         "client_secret":"aabbcc..."}'
Risposta
{
  "success":      true,
  "access_token": "eyJhbGciOiJIUzI1NiJ9...",
  "token_type":   "Bearer",
  "expires_in":   86400,
  "expires_at":   "2026-02-27T14:30:00+01:00",
  "client_name":  "AgileApp"
}

POST ?action=richiesta

Avvia la richiesta di un documento. Il costo viene addebitato immediatamente. La risposta contiene l'id da usare per il polling.

Parametri body (JSON)

CampoTipoNote
vatstringobbligatorio P.IVA italiana (11 cifre)
typestringobbligatorio visura_camerale · visura_storica · atto_ottico · statuto
protocolstringobbligatorio per atti Es. MO-2025-5323
investigation_idintopzionale ID informativa CertiSource
dossier_idintopzionale ID dossier CertiSource
webhook_urlstringopzionale URL HTTPS a cui CertiSource invierà un POST al completamento (niente polling)
webhook_secretstringopzionale Segreto HMAC-SHA256 per verificare la firma X-CertiSource-Signature
forceboolopzionale true = bypassa la cache e acquista sempre un nuovo documento (costo pieno)
cURL — Visura camerale
curl -X POST "https://certisource.it/atti-service.php?action=richiesta" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"vat":"04058890361","type":"visura_camerale"}'
JavaScript (fetch)
const res = await fetch("https://certisource.it/atti-service.php?action=richiesta", {
  method: "POST",
  headers: {
    "Authorization": "Bearer cs_pat_IL_TUO_TOKEN",
    "Content-Type":  "application/json"
  },
  body: JSON.stringify({ vat: "04058890361", type: "visura_camerale" })
});
const data = await res.json();
console.log(data.id, data.cost); // id da usare per polling, costo addebitato
PHP (cURL)
$ch = curl_init("https://certisource.it/atti-service.php?action=richiesta");
curl_setopt_array($ch, [
  CURLOPT_POST           => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => [
    "Authorization: Bearer cs_pat_IL_TUO_TOKEN",
    "Content-Type: application/json"
  ],
  CURLOPT_POSTFIELDS => json_encode([
    "vat"  => "04058890361",
    "type" => "visura_camerale"
  ])
]);
$data = json_decode(curl_exec($ch), true);
Risposta — nuovo documento
{
  "success":      true,
  "id":           42,            // usa per stato, scarica, dati
  "provider_id":  "uuid-del-provider",
  "status":       "processing",
  "cached":       false,
  "cost":         4.95,
  "currency":     "EUR",
  "data_url":     "?action=dati&id=42",
  "message":      "Chiama ?action=stato&id=42 ogni 15s"
}
Risposta — documento dalla cache (cost=0)
{
  "success":      true,
  "id":           43,
  "cached":       true,
  "cached_from":  38,            // id della richiesta originale
  "status":       "completed",
  "cost":         0.00,          // nessun addebito
  "currency":     "EUR",
  "download_url": "?action=scarica&id=43",
  "data_url":     "?action=dati&id=43"
}
⚠️ Se il credito non è sufficiente la risposta è HTTP 402 con "code": "INSUFFICIENT_CREDIT". Nessun addebito viene effettuato.

GET ?action=stato&id={id}

Controlla lo stato di una richiesta. Da chiamare ogni 15 secondi fino a "completed": true.

cURL
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=stato&id=42"

Risposta — In elaborazione

JSON
{
  "success":   true,
  "id":        42,
  "status":    "In erogazione",
  "completed": false,
  "message":   "Documento in elaborazione..."
}

Risposta — Completata

JSON
{
  "success":      true,
  "id":           42,
  "status":       "completed",
  "completed":    true,
  "file_name":    "visura_camerale_04058890361_20260226.pdf",
  "download_url": "?action=scarica&id=42&token=..."
}
Pattern polling consigliato
JavaScript
async function pollUntilReady(id, token, intervalMs = 15000, maxAttempts = 40) {
  for (let i = 0; i < maxAttempts; i++) {
    const res  = await fetch(`?action=stato&id=${id}`, {
      headers: { "Authorization": `Bearer ${token}` }
    });
    const data = await res.json();

    if (data.completed)            return data;    // ✓ pronto
    if (data.status === "failed") throw new Error(data.error); // ✗ errore

    await new Promise(r => setTimeout(r, intervalMs));
  }
  throw new Error("Timeout: documento non pronto dopo 10 minuti");
}

GET ?action=scarica&id={id}

Scarica il PDF di una richiesta completata. Risponde con Content-Type: application/pdf.

cURL — salva su file
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=scarica&id=42" \
     -o visura.pdf
ℹ️ Il parametro &token=... nel download_url restituito da stato permette di aprire il PDF direttamente in una nuova scheda del browser senza header aggiuntivi.

GET ?action=lista

Elenco delle richieste dell'account autenticato (o filtrate per informativa).

Query string opzionale

ParamDefaultNote
investigation_idFiltra per ID informativa
limit50Max 200
offset0Per paginazione
Risposta
{
  "success": true,
  "total":   3,
  "items": [
    {
      "id":            42,
      "document_type": "visura_camerale",
      "vat_number":    "04058890361",
      "status":        "completed",
      "cost_eur":      4.95,
      "created_at":    "2026-02-26 14:00:00"
    }
  ]
}

GET ?action=dati&id={id}

Restituisce i dati strutturati estratti dalla visura: ragione sociale, codice fiscale, P.IVA, forma giuridica, sedi (array), PEC, oggetto sociale, codici ATECO multipli, cariche con dati anagrafici completi. Disponibile per visura_camerale e visura_storica. Se i dati non sono ancora stati parsati ma il PDF esiste, il parsing viene eseguito on-demand.

Tipo documentoDati strutturatiCostoQuando usarla
visura_camerale ✓ Consigliata Dati correnti: ragione sociale, sede legale + unità locali, REA, cariche con anagrafica completa, soci/quote, ATECO primari+secondari, PEC, oggetto sociale, capitale sociale, numero addetti, poteri di rappresentanza € 4,95 Onboarding clienti, KYC, arricchimento CRM — uso tipico
visura_storica Tutti i dati della camerale + storia completa degli eventi societari dalla fondazione (nomine, cessioni, variazioni capitale, procedure concorsuali, ecc.) € 5,90 Due diligence, analisi governance storica, monitoraggio procedure concorsuali
cURL
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=dati&id=42"
JavaScript
const res  = await fetch(`https://certisource.it/atti-service.php?action=dati&id=${id}`, {
  headers: { "Authorization": "Bearer cs_pat_IL_TUO_TOKEN" }
});
const data = await res.json();
const sd   = data.structured_data;

// Pre-compila il form di onboarding
document.getElementById("ragione_sociale").value = sd.ragione_sociale;
document.getElementById("codice_fiscale").value  = sd.codice_fiscale;
document.getElementById("pec").value              = sd.pec;
const sedeLegale = sd.sedi?.find(s => s.tipo === 'sede_legale');
document.getElementById("comune").value             = sedeLegale?.comune;
document.getElementById("ateco_primario").value   = sd.ateco_codes[0]?.code;
Risposta
{
  "success":       true,
  "id":            42,
  "document_type": "visura_camerale",
  "vat_number":    "04058890361",
  "structured_data": {
    "ragione_sociale":   "ACME SOCIETA' COOPERATIVA S.R.L.",
    "forma_giuridica":   "srl",
    "codice_fiscale":    "04058890361",     // estratto dalla sez.1 del PDF
    "partita_iva":       "04058890361",
    "pec":               "acme@pec.it",
    "rea":               "MO - 381232",
    "sedi": [            // array: sede_legale sempre prima, poi unità locali
      {
        "tipo":      "sede_legale",
        "via":       "Via Roma 1",
        "cap":       "41100",
        "comune":    "Modena",
        "provincia": "MO",
        "pec":       "acme@pec.it"
      },
      {
        "tipo":        "unita_locale",
        "via":         "Via Verdi 5",
        "cap":         "40100",
        "comune":      "Bologna",
        "provincia":   "BO",
        "attivita":    "Magazzino e logistica",
        "ateco_codes": [  // codici ATECO specifici di questa unità locale (se presenti nella visura)
          { "code": "41.20.00", "description": "Costruzione di edifici residenziali", "type": "primary" }
        ]
      }
    ],
    "oggetto_sociale":   "Costruzione, ristrutturazione e vendita di edifici civili e industriali. La società potrà compiere tutte le operazioni commerciali, industriali, finanziarie, mobiliari e immobiliari necessarie o utili per il raggiungimento dell'oggetto sociale...",
    "ateco_codes": [
      { "code": "41.20.00", "description": "Costruzione di edifici residenziali", "type": "primary"   },
      { "code": "43.31.00", "description": "Intonacatura e stuccatura",            "type": "secondary" }
    ],
    "capitale_sociale":  50000,
    "numero_addetti":    127,              // media annua addetti (da fonte INPS, se disponibile)
    "addetti": {                            // dettaglio dipendenti/indipendenti da fonte INPS — null se non presente
      "fonte":          "INPS",
      "anno":           2024,
      "data_rilievo":   "2024-09-30",
      "trimestri": {
        "dipendenti":    { "q1": 120, "q2": 125, "q3": 130, "media": 125 },
        "indipendenti":  { "q1": 2,   "q2": 2,   "q3": 2,   "media": 2   },
        "totale":        { "q1": 122, "q2": 127, "q3": 132, "media": 127 }
      },
      "totale_medio":   127,              // coincide con numero_addetti
      "addetti_per_comune": [             // distribuzione per sede
        { "comune": "MODENA", "provincia": "MO", "tipo": "sede_legale" },
        { "comune": "ROMA",   "provincia": "RM", "tipo": "unita_locale" }
      ],
      "distribuzione_contratto": [        // % per tipo contratto (Q1,Q2,Q3) — null se dati non presenti (es. azienda con un solo tipo contratto)
        { "tipo": "Tempo Indeterminato", "q1": 85.3, "q2": 88.0, "q3": 87.5 },
        { "tipo": "Tempo Determinato",   "q1": 14.7, "q2": 12.0, "q3": 12.5 }
      ],
      "distribuzione_orario": [           // % per orario di lavoro — null se non presente
        { "tipo": "Tempo Pieno",   "q1": 92.0, "q2": 91.0, "q3": 93.0 },
        { "tipo": "Tempo Parziale", "q1": 8.0,  "q2": 9.0,  "q3": 7.0  }
      ],
      "distribuzione_qualifica": [        // % per qualifica — null se non presente
        { "tipo": "Impiegato",   "q1": 68.0, "q2": 70.0, "q3": 69.0 },
        { "tipo": "Quadro",      "q1": 20.0, "q2": 18.0, "q3": 19.0 },
        { "tipo": "Apprendista", "q1": 12.0, "q2": 12.0, "q3": 12.0 }
      ]
    },
    "data_costituzione": "1998-03-15",
    "data_scadenza":     null,             // data termine societaria, se presente (es. spa a tempo determinato)
    "data_rilascio":     "2026-02-27",    // data ufficiale di emissione del documento CCIAA
    "soci": [            // sez. 7 — presenti solo se indicati nella visura (es. SRL, SPA)
      {
        "tipo":             "persona_fisica",   // "persona_fisica" | "persona_giuridica"
        "nome_completo":    "BIANCHI LUCIA",
        "codice_fiscale":   "BNCLCU75B48F205Z",
        "nato_a":           "Milano",
        "provincia_nascita":"MI",
        "data_nascita":     "1975-02-08",
        "tipo_rapporto":    "Socio",
        "quota_eur":        5000,          // quota in EUR (SRL/cooperative)
        "percentuale":      10.00,           // % capitale sociale
        "data_iscrizione":  "2010-06-01"
      },
      {
        "tipo":             "persona_giuridica",
        "ragione_sociale":  "HOLDING NORD S.R.L.",
        "codice_fiscale":   "12345678901",
        "tipo_rapporto":    "Socio",
        "quota_eur":        45000,
        "percentuale":      90.00,
        "num_azioni":       9000,           // presente solo per SPA (numero azioni)
        "data_iscrizione":  "2010-06-01"
      }
    ],
    "poteri_rappresentanza": "Il Presidente del Consiglio di Amministrazione ha la rappresentanza legale della società di fronte a terzi e in giudizio, con firma singola per operazioni fino a Euro 50.000; oltre tale importo è richiesta la firma congiunta di un secondo amministratore delegato.",
                             // null se non estratto dalla visura
    "cariche": [
      {
        "nome_completo":       "ROSSI MARIO",
        "cognome":             "ROSSI",
        "nome":                "MARIO",
        "codice_fiscale":      "RSSMRA70A01F205X",
        "genere":              "M",
        "data_nascita":        "1970-01-01",
        "luogo_nascita":       "Milano (MI)",
        "domicilio":           "Via Verdi 3 CAP 41100",
        "ruolo":               "Amministratore Unico",
        "tutti_i_ruoli":       ["Amministratore Unico"],
        "data_nomina":         "2020-01-15",
        "data_cessazione":     null,            // data fine carica (presente nella visura storica)
        "pec":                 "rossi@pec.it",  // PEC personale dell'amministratore, se presente
        "rappresentante_legale": true,
        "contesto":            "administrator"
      },
      {
        "nome_completo":   "TESTI GIULIO",
        "codice_fiscale":  "TSTGLL68T12F257H",
        "ruolo":           "curatore",
        "data_nomina":     "2025-01-15",
        "contesto":        "procedure"   // "procedure" = soggetto in procedura concorsuale
      }
    ],
    "eventi": [           // presente solo per visura_storica — storia completa dalla fondazione
      {
        "data":        "1998-03-15",
        "tipo":        "costituzione",
        "categoria":   "structural",
        "descrizione": "Atto costitutivo - Notaio MARIO BIANCHI",
        "dettagli":    { "notaio": "MARIO BIANCHI" }
      },
      {
        "data":        "2020-01-15",
        "tipo":        "nomina",
        "categoria":   "governance",
        "soggetto":    "ROSSI MARIO",
        "descrizione": "Nomina Amministratore Unico: ROSSI MARIO"
      },
      {
        "data":        "2022-06-30",
        "tipo":        "deposito_bilancio",
        "categoria":   "financial",
        "descrizione": "Deposito bilancio esercizio 2021"
      }
    ],                     // null per visura_camerale
    "source":    "parsed_from_pdf",
    "parsed_at": "2026-02-27T10:00:00+01:00"
  }
}
💡 data_rilascio: data ufficiale di emissione del documento CCIAA (formato YYYY-MM-DD), estratta dall'intestazione del PDF o, come fallback, dalla data di acquisizione. Usare questo campo per valutare la "freschezza" del documento nei propri processi di compliance.

Validità per contesto d'uso — non esiste una legge che stabilisce una validità universale: la CCIAA emette il documento "alla data odierna" senza scadenza. In pratica: uso KYC/CRM → 6 mesi comunemente accettati; gare d'appalto pubbliche (D.Lgs. 36/2023) → tipicamente 3-6 mesi; pratiche bancarie → 3 mesi; atti notarili/giudiziari → richiedere con "force":true per documento fresco.

rea: numero REA nel formato "XX - 123456" dove XX è la sigla della Camera di Commercio (es. "RM - 381232"). Estratto dalla sezione 1 del PDF.

sedi[] è un array: il primo elemento è sempre la sede legale ("tipo": "sede_legale"), i successivi sono le unità locali ("tipo": "unita_locale"). Ogni sede ha: via, cap, comune, provincia. Le unità locali hanno il campo aggiuntivo attivita (tipo attività svolta) e ateco_codes[] con i codici ATECO specifici di quella sede (quando registrati al CCIAA — stessa struttura di ateco_codes aziendale). Tutte le unità locali registrate alla CCIAA sono incluse.

soci[] rispecchia la sezione "Soci e titolari" della visura: presente per SRL e SPA, assente (array vuoto) per forme giuridiche che non pubblicano i soci (es. cooperative con molti soci, consorzi, fondazioni). Ogni elemento distingue persona_fisica (campi nome, nascita, CF) da persona_giuridica (campo ragione_sociale). Il campo num_azioni è presente solo per SPA (in alternativa a quota_eur). Il campo percentuale è il float della % di partecipazione.

poteri_rappresentanza: testo verbatim estratto dalla visura (sez. "Poteri di rappresentanza" o simile). Può essere null se assente o non riconosciuto nel layout del PDF.

cariche[] include tutti i campi anagrafici disponibili: nome, cognome, CF, data e luogo di nascita, domicilio, PEC personale (se presente nella visura). Il campo data_cessazione indica la fine del mandato (presente nella visura storica per cariche cessate). contesto è "administrator" per cariche societarie normali e "procedure" per soggetti nominati in procedure concorsuali (curatore, liquidatore, ecc.).

eventi[]: presente solo per visura_storica — contiene la storia completa degli eventi societari dalla fondazione. Ogni evento ha: data (YYYY-MM-DD), tipo (es. nomina, deposito_bilancio, cambio_sede), categoria (structural/governance/financial/operational/compliance/ownership), soggetto (nome persona/società coinvolta, opzionale), descrizione (testo sintetico), dettagli (oggetto con info aggiuntive, es. notaio, giudice delegato). Per la visura_camerale questo campo è null.

oggetto_sociale non viene troncato — può essere molto lungo (anche 2000+ caratteri). data_scadenza è la data di termine societaria (presente solo per società a tempo determinato, null nella grande maggioranza dei casi).

numero_addetti e addetti: estratti dalla sezione "Distribuzione dipendenti (elaborazione da fonte INPS)" della visura. numero_addetti contiene la media annua totale. addetti è null se la sezione INPS è assente nel documento, altrimenti contiene: anno, data_rilievo, trimestri (con breakdown dipendenti/indipendenti/totale per Q1, Q2, Q3 e media annua), totale_medio (coincide con numero_addetti), addetti_per_comune (distribuzione per sede) e le tre distribuzioni percentuali trimestrali:
  • distribuzione_contratto[]: % per tipo contratto (es. Tempo Indeterminato, Tempo Determinato)
  • distribuzione_orario[]: % per orario di lavoro (Tempo Pieno / Tempo Parziale)
  • distribuzione_qualifica[]: % per qualifica (Apprendista, Impiegato, Quadro, ecc.)
Ciascuna distribuzione è null quando il PDF non riporta valori percentuali per quella categoria (tipico in aziende con dipendenti omogenei per tipo/orario/qualifica).

Codici di stato

HTTPSignificato
200Dati strutturati disponibili
202Documento ancora in elaborazione — riprova dopo il completamento
422Tipo documento non supporta il parsing (solo visure)
404File PDF non trovato su disco

Guida: Dati strutturati — Quickstart

Vuoi ottenere in JSON i dati anagrafici di un'azienda italiana (ragione sociale, CF, P.IVA, sede, ATECO, cariche) a partire dalla sola Partita IVA? Questo è il flusso minimo.

💡 Usa visura_camerale, non la visura storica: contiene gli stessi dati anagrafici attuali, costa meno (€ 4,95 vs € 5,90) ed è più veloce. La visura storica serve solo se hai bisogno anche della storia completa degli eventi societari.
1

Richiedi la visura camerale

Invia il body con la P.IVA dell'azienda e "type": "visura_camerale". Se la stessa P.IVA è già stata richiesta nelle ultime 24h, il documento viene restituito dalla cache a costo zero (campo "cached": true).

cURL — POST richiesta
curl -X POST "https://certisource.it/atti-service.php?action=richiesta" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"vat":"01234567890","type":"visura_camerale"}'

// Risposta immediata
{
  "success":    true,
  "id":         42,          // salva questo id
  "status":     "processing",
  "cached":     false,
  "cost":       4.95,
  "data_url":   "?action=dati&id=42"
}

Se "cached": true → salta al passo 3, i dati sono già disponibili.

2

Fai polling su stato ogni 15 secondi

La visura camerale è pronta tipicamente in 30–120 secondi. Continua a interrogare ?action=stato finché "completed": true.

cURL — GET stato
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=stato&id=42"

// Quando pronta:
{
  "completed":    true,
  "status":       "completed",
  "download_url": "?action=scarica&id=42&token=...",
  "data_url":     "?action=dati&id=42"
}

In alternativa: usa i webhook (vedi Guida webhook) per ricevere una notifica push ed evitare il polling.

3

Leggi i dati strutturati JSON

Chiama ?action=dati&id=42. Il parsing viene eseguito automaticamente alla prima chiamata, poi i risultati sono in cache nel database CertiSource.

cURL — GET dati
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=dati&id=42"

{
  "success": true,
  "structured_data": {
    "ragione_sociale":  "ACME SRL",
    "forma_giuridica":  "srl",
    "codice_fiscale":   "01234567890",
    "partita_iva":      "01234567890",
    "pec":              "acme@pec.it",
    "rea":              "MI - 2345678",
    "sedi": [
      { "tipo": "sede_legale", "via": "Via Montenapoleone 1", "comune": "Milano", "provincia": "MI", "cap": "20121" },
      { "tipo": "unita_locale", "via": "Via Roma 5", "comune": "Torino", "provincia": "TO", "attivita": "Magazzino",
        "ateco_codes": [{ "code": "62.01.00", "description": "Produzione di software", "type": "primary" }] }
    ],
    "ateco_codes": [{ "code": "62.01.00", "description": "Produzione di software", "type": "primary" }],
    "capitale_sociale": 10000,
    "data_rilascio":  "2026-02-27",  // data di emissione ufficiale CCIAA
    "cariche": [{
      "nome_completo":    "ROSSI MARIO",
      "codice_fiscale":   "RSSMRA70A01F205X",
      "ruolo":            "Amministratore Unico",
      "data_nomina":      "2020-01-15",
      "rappresentante_legale": true
    }],
    "soci": [{ "tipo": "persona_fisica", "nome_completo": "ROSSI MARIO", "quota_eur": 10000, "percentuale": 100.0 }],
    "eventi": null  // null per visura_camerale; array per visura_storica
  }
}

Flusso completo in PHP

PHP — Esempio completo
function getDatiAzienda(string $piva, string $pat): array {
    $base = 'https://certisource.it/atti-service.php';
    $auth = ['Authorization: Bearer ' . $pat, 'Content-Type: application/json'];

    // 1. Richiesta
    $ch = curl_init($base . '?action=richiesta');
    curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_HTTPHEADER=>$auth,
        CURLOPT_RETURNTRANSFER=>true,
        CURLOPT_POSTFIELDS=>json_encode(['vat'=>$piva, 'type'=>'visura_camerale'])]);
    $r = json_decode(curl_exec($ch), true);
    curl_close($ch);
    if (!$r['success']) throw new \Exception($r['error'] ?? 'Errore richiesta');
    $id = $r['id'];
    if ($r['cached'] ?? false) goto dati; // cache hit → salta il polling

    // 2. Polling (max 3 minuti)
    for ($i=0; $i<12; $i++) {
        sleep(15);
        $ch = curl_init($base . '?action=stato&id=' . $id);
        curl_setopt_array($ch, [CURLOPT_HTTPHEADER=>$auth, CURLOPT_RETURNTRANSFER=>true]);
        $s = json_decode(curl_exec($ch), true); curl_close($ch);
        if ($s['completed'] ?? false) break;
    }

    // 3. Dati strutturati
    dati:
    $ch = curl_init($base . '?action=dati&id=' . $id);
    curl_setopt_array($ch, [CURLOPT_HTTPHEADER=>$auth, CURLOPT_RETURNTRANSFER=>true]);
    $d = json_decode(curl_exec($ch), true); curl_close($ch);
    return $d['structured_data'] ?? [];
}

// Uso:
$dati = getDatiAzienda('01234567890', getenv('CERTISOURCE_PAT'));
echo $dati['ragione_sociale'];    // "ACME SRL"
echo $dati['codice_fiscale'];    // "01234567890"
echo $dati['cariche'][0]['ruolo']; // "Amministratore Unico"

Note importanti

  • La cache è automatica: se chiami la stessa P.IVA entro 24h, ricevi i dati in <1 secondo a costo zero.
  • codice_fiscale: per le persone fisiche coincide con la P.IVA. Il campo è estratto direttamente dal PDF della visura (sezione 1).
  • oggetto_sociale: non viene troncato — può contenere 2000+ caratteri. Filtra i newline (\n) se necessario.
  • sedi[]: il primo elemento è sempre "tipo": "sede_legale", i successivi sono unità locali (se presenti nella visura).
  • cariche[]: include tutti i campi anagrafici disponibili nel PDF. Il campo codice_fiscale per ogni persona è quello del soggetto (non dell'azienda).
  • numero_addetti + addetti: estratti dalla sezione INPS della visura (non sempre presente). addetti è null se assente; altrimenti include: dettaglio trimestrale dipendenti/indipendenti/totale, addetti_per_comune, e le distribuzioni % per contratto, orario e qualifica (null quando il PDF non riporta valori, tipico in aziende omogenee).

Guida: Personal API Token

Il modo più semplice per integrare un'applicazione che agisce per conto di un utente CertiSource con credito proprio.

1

Genera il token dal tuo account CertiSource

Vai su Profilo → Token API e crea un nuovo token con un nome che identifichi l'applicazione (es. "AgileApp ERP").

API (se integri via backend)
curl -X POST "https://certisource.it/api/auth/api-tokens" \
     -H "Authorization: Bearer <jwt-sessione-certisource>" \
     -H "Content-Type: application/json" \
     -d '{"name":"AgileApp","permissions":["richiesta","stato","scarica"]}'

// Risposta — il token appare UNA SOLA VOLTA:
{
  "token":   "cs_pat_9580bfab...",
  "warning": "Salva subito: non sarà più visibile"
}
2

Incolla il token nella tua applicazione

Conserva il token in una variabile d'ambiente o un secret store. Non hardcodarlo nel codice sorgente.

.env
CERTISOURCE_API_TOKEN=cs_pat_9580bfab2c71a...
3

Usalo nelle chiamate API

Aggiungi l'header Authorization: Bearer cs_pat_... ad ogni richiesta.

cURL
curl -X POST "https://certisource.it/atti-service.php?action=richiesta" \
     -H "Authorization: Bearer $CERTISOURCE_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"vat":"04058890361","type":"visura_camerale"}'
🔑 Se il token viene compromesso, revocalo subito da Profilo → Token API e genera un nuovo token. L'applicazione che lo usava riceverà HTTP 401 su ogni richiesta successiva.

Ciclo di vita del token

FaseChi agisceCosa fare
Creazione Titolare account CertiSource Vai su Profilo → Token API → + Genera token. Scegli nome, permessi e scadenza.
Consegna Titolare account CertiSource Il token raw (cs_pat_…) è visibile una sola volta. Copialo e invialo all'amministratore tecnico dell'applicazione tramite canale sicuro (password manager condiviso, email cifrata, ecc.).
Configurazione Amministratore tecnico app esterna Salva il token come variabile d'ambiente (CERTISOURCE_TOKEN=cs_pat_…). Non includerlo nel codice sorgente né nei log.
Monitoraggio scadenza Entrambi Il campo expires_at è incluso nella risposta GET /api/auth/api-tokens. Imposta un alert nel tuo sistema di monitoring per almeno 30 giorni prima della scadenza.
Rotazione Titolare account CertiSource Genera il nuovo token → consegnalo all'app esterna → aggiorna la variabile d'ambiente → revoca il vecchio token. Ordine importante: aggiorna prima il nuovo, poi revoca il vecchio.
Revoca emergenza Titolare account CertiSource Clicca Revoca nella pagina Token API. Effetto immediato: tutte le chiamate successive con quel token ricevono HTTP 401.
📋
Checklist per l'amministratore tecnico dell'applicazione integrata:
  • Salvare expires_at alla ricezione del token
  • Configurare un alert 30 giorni prima della scadenza (cron, monitoring, reminder in calendario)
  • Contattare il referente CertiSource (titolare account) per ricevere il nuovo token prima della scadenza
  • Verificare che HTTP 401 in produzione triggeri una notifica immediata al team (token scaduto o revocato)
Come leggere expires_at dalla lista token
GET /api/auth/api-tokens
Authorization: Bearer <jwt-sessione-certisource>

// Risposta:
{
  "tokens": [{
    "id":           2,
    "name":         "Lg231.certisource.it",
    "permissions":  ["richiesta", "stato", "scarica"],
    "expires_at":   "2027-02-27T00:00:00",  // null = nessuna scadenza
    "last_used_at": "2026-02-27T10:14:00",
    "is_active":    1
  }]
}

Endpoint: Verifica PAT

Controlla se un Personal API Token è valido, scaduto o revocato — senza consumare credito né generare richieste. Utile per health-check all'avvio dell'applicazione o per mostrare messaggi di errore chiari all'utente.

cURL
curl "https://certisource.it/atti-service.php?action=verifica-pat" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN"

La risposta è sempre HTTP 200. Controlla il campo valid e status:

statusvalidSignificato
activetrueToken valido e operativo
expiredfalseToken scaduto — rinnova o genera uno nuovo
revokedfalseToken disattivato manualmente dall'utente
user_suspendedfalseAccount utente sospeso
not_foundfalseToken non esiste nel sistema
no_tokenfalseHeader Authorization mancante
Risposta — token attivo
{
  "valid":        true,
  "status":       "active",
  "name":         "Lg231",
  "permissions":  { "read": true, "write": true },
  "expires_at":   null,          // null = nessuna scadenza
  "last_used_at": "2026-02-27T12:47:53"
}

// Token scaduto:
{ "valid": false, "status": "expired", "name": "Lg231", "expires_at": "2026-01-01T00:00:00" }

// Token non trovato:
{ "valid": false, "status": "not_found" }
💡 Questo endpoint non aggiorna last_used_at e non addebita nulla. Ideale per health-check periodici o per validare il token al bootstrap della tua applicazione.

Guida: Integrazione B2B (Client Credentials)

Per applicazioni che non hanno un utente CertiSource associato. Le credenziali client vengono fornite da CertiSource.

1

Ricevi client_id e client_secret

CertiSource genera le credenziali e te le fornisce una tantum (il secret non è recuperabile). Conservale in un secret store.

.env
CS_CLIENT_ID=2d5c2723-294c-4a80-a61e-90e224c466ba
CS_CLIENT_SECRET=54244aee8e48249600e233966b7a1bbf...
2

Ottieni il JWT (all'avvio o quando scade)

cURL
curl -X POST "https://certisource.it/atti-service.php?action=token" \
     -H "Content-Type: application/json" \
     -d '{"client_id":"$CS_CLIENT_ID","client_secret":"$CS_CLIENT_SECRET"}'

// Risposta:
{ "access_token": "eyJ...", "expires_in": 86400 }
3

Usa il JWT — rinnovalo prima della scadenza

Il JWT scade dopo expires_in secondi. Salva la scadenza e rinnova il token qualche minuto prima. Il pattern di rinnovo automatico:

JavaScript
let token = null, tokenExpiry = 0;

async function getToken() {
  if (Date.now() < tokenExpiry - 60000) return token; // rinnova 1 min prima
  const res = await fetch("?action=token", {
    method: "POST",
    body: JSON.stringify({ client_id: CS_CLIENT_ID, client_secret: CS_SECRET }),
    headers: { "Content-Type": "application/json" }
  });
  const d = await res.json();
  token = d.access_token;
  tokenExpiry = Date.now() + d.expires_in * 1000;
  return token;
}

Guida: Esempio completo (visura)

Flusso end-to-end: richiesta → polling → download del PDF in PHP.

PHP — Flusso completo
/**
 * Scarica una visura camerale per P.IVA.
 * Salva il PDF nella directory indicata.
 */
function scaricaVisura(string $vat, string $token, string $outputDir): string
{
    define('API', 'https://certisource.it/atti-service.php');
    define('AUTH', ['Authorization: Bearer ' . $token, 'Content-Type: application/json']);

    // 1. Richiesta documento
    $ch = curl_init(API . '?action=richiesta');
    curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => AUTH,
        CURLOPT_POSTFIELDS => json_encode(['vat' => $vat, 'type' => 'visura_camerale'])]);
    $res = json_decode(curl_exec($ch), true);

    if (!$res['success']) {
        throw new \RuntimeException($res['error'] ?? 'Errore richiesta');
    }
    $id = $res['id'];
    echo "Richiesta #{$id} inviata. Costo: €{$res['cost']}\n";

    // 2. Polling ogni 15s (max 10 minuti)
    for ($i = 0; $i < 40; $i++) {
        sleep(15);
        $ch = curl_init(API . "?action=stato&id={$id}");
        curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => AUTH]);
        $stato = json_decode(curl_exec($ch), true);

        if ($stato['completed']) break;
        if ($stato['status'] === 'failed') throw new \RuntimeException($stato['error']);
        echo "Stato: {$stato['status']}...\n";
    }

    if (!$stato['completed']) throw new \RuntimeException('Timeout');

    // 3. Scarica PDF
    $ch = curl_init(API . "?action=scarica&id={$id}");
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => AUTH]);
    $pdf = curl_exec($ch);

    $path = rtrim($outputDir, '/') . "/visura_{$vat}.pdf";
    file_put_contents($path, $pdf);
    echo "PDF salvato: {$path}\n";
    return $path;
}

// Utilizzo:
scaricaVisura('04058890361', getenv('CERTISOURCE_API_TOKEN'), __DIR__ . '/downloads');

Guida: Webhook (event-driven)

Invece di interrogare ripetutamente ?action=stato puoi ricevere una notifica push quando il documento è pronto. CertiSource invierà un POST al tuo endpoint non appena la visura viene completata.

1

Aggiungi webhook_url alla richiesta

cURL
curl -X POST "https://certisource.it/atti-service.php?action=richiesta" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "vat": "04058890361",
       "type": "visura_camerale",
       "webhook_url": "https://tua-app.it/callbacks/certisource",
       "webhook_secret": "un_segreto_condiviso"
     }'
2

Ricevi il webhook nel tuo endpoint

CertiSource invierà un POST con i seguenti header:

HeaderValore
Content-Typeapplication/json
X-CertiSource-Eventatti_request.completed oppure atti_request.failed
X-CertiSource-Signaturesha256=<hmac> — presente solo se hai impostato webhook_secret
X-CertiSource-AttemptNumero del tentativo corrente (1, 2 o 3). Utile per gestire i duplicati in caso di retry.
Payload webhook
{
  "event":        "atti_request.completed",
  "id":           42,
  "status":       "completed",
  "document_type":"visura_camerale",
  "vat_number":   "04058890361",
  "file_name":    "visura_camerale_123_20260226.pdf",
  "cost_eur":     4.95,
  "completed_at": "2026-02-27T12:47:53",
  "cached":       false,
  "download_url": "https://certisource.it/atti-service.php?action=scarica&id=42",
  "data_url":     "https://certisource.it/atti-service.php?action=dati&id=42"
}

Il tuo endpoint deve rispondere con HTTP 2xx entro 10 secondi per confermare la ricezione. Qualsiasi altro codice (o timeout) viene considerato un fallimento e innesca il retry automatico.

3

Verifica la firma HMAC (opzionale ma consigliato)

PHP — verifica firma
$secret    = 'un_segreto_condiviso';
$payload   = file_get_contents('php://input');
$incomingSig = $_SERVER['HTTP_X_CERTISOURCE_SIGNATURE'] ?? '';
$expected    = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $incomingSig)) {
    http_response_code(403);
    exit('Firma non valida');
}

$data = json_decode($payload, true);
// $data['id'] → id richiesta, $data['data_url'] → endpoint dati strutturati
💡
Consegna garantita con retry automatico.
Il webhook viene inviato immediatamente al completamento del documento. Se il tuo endpoint non risponde con 2xx, il sistema riprova automaticamente fino a 3 volte (ogni 5 minuti). Dopo 3 tentativi falliti l'evento è considerato abbandonato — puoi comunque recuperare il documento con ?action=stato o ?action=scarica.

Importante per l'idempotenza: potresti ricevere lo stesso evento più volte (retry). Usa id + X-CertiSource-Attempt per deduplicare sul tuo sistema.

Guida: Codici ATECO multipli e per sede

Ogni visura può contenere un codice ATECO primario (attività prevalente) e uno o più codici secondari a livello aziendale. Inoltre, ogni unità locale ha i propri codici ATECO registrati alla Camera di Commercio territoriale competente.

ATECO aziendali (livello azienda)

Il campo ateco_codes a livello top è un array ordinato: primo elemento = primario, gli altri = secondari.

Struttura ateco_codes aziendale
// structured_data.ateco_codes — attività prevalente + secondarie dell'azienda
[
  { "code": "62.10.00", "description": "Attività di programmazione informatica", "type": "primary"   },
  { "code": "63.10.10", "description": "Fornitura di infrastrutture informatiche e hosting", "type": "secondary" },
  { "code": "63.91.00", "description": "Attività dei portali di ricerca sul web", "type": "secondary" }
]

ATECO per unità locale (livello sede)

Ogni elemento di sedi[] di tipo unita_locale include il campo ateco_codes[] con i codici registrati presso la CCIAA di competenza di quella sede. Utile per sapere cosa fa concretamente ciascuna sede (es. sede di Roma fa consulenza, sede di Modena fa produzione).

Struttura sedi[] con ATECO per sede
// structured_data.sedi[] — sede_legale non ha ateco_codes, unità locali sì
[
  { "tipo": "sede_legale", "comune": "Roma", "provincia": "RM", "via": "Piazza di Campitelli 2" },
  {
    "tipo":        "unita_locale",
    "comune":      "Modena",
    "provincia":   "MO",
    "via":         "Strada Scaglia Est 15",
    "cap":         "41126",
    "ateco_codes": [
      { "code": "62.10.00", "description": "Attività di programmazione informatica", "type": "primary" }
    ]
  }
]
JavaScript — uso pratico (ATECO aziendali + per sede)
const sd = data.structured_data;

// ATECO aziendali
const primario    = sd.ateco_codes.find(c => c.type === 'primary');
const secondari   = sd.ateco_codes.filter(c => c.type === 'secondary');
const tuttiCodici = sd.ateco_codes.map(c => c.code);  // es. ['62.10.00', '63.10.10']

// ATECO per singola unità locale
const unitaLocali = sd.sedi.filter(s => s.tipo === 'unita_locale');
unitaLocali.forEach(sede => {
  const atecoSede = sede.ateco_codes ?? [];  // può essere assente se non registrato
  console.log(sede.comune, atecoSede.map(a => a.code));
});

// Matching template 231 per settore (usa ATECO prevalente)
const templateId = getTemplateByAteco(primario?.code);  // tua logica
💡 Classificazione ATECO 2025: CertiSource estrae i codici dalla sezione "Classificazione ATECO 2025" del PDF (e il compatibile ATECORI 2007-2022). Se una visura ha codici di entrambe le classificazioni, vengono restituiti tutti — la deduplicazione è automatica. I codici ATECO per unità locale sono quelli registrati al CCIAA territoriale di competenza (possono differire da quelli della sede legale).

GET ?action=credito

Restituisce il saldo credito dell'utente autenticato (reale + voucher) e lo storico degli ultimi movimenti. Richiede autenticazione utente (PAT obbligatorio — non disponibile con API Key B2B senza utente associato).

Parametri query (opzionali)

ParamDefaultNote
limit20N° movimenti da restituire (max 100)
order_idSe presente: verifica stato di un ordine di ricarica specifico
cURL — saldo + movimenti
curl -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     "https://certisource.it/atti-service.php?action=credito&limit=10"
Risposta — saldo
{
  "success": true,
  "credito": {
    "reale":   45.60,   // credito acquistato con pagamento
    "voucher": 10.00,   // credito promozionale (usato per primo)
    "totale":  55.60    // da usare nei documenti
  },
  "ultimi_movimenti": [
    {
      "id":               88,
      "transaction_type": "investigation_debit",
      "credit_type":      "voucher",
      "amount":           -4.95,
      "balance_after":    55.60,
      "description":      "Richiesta Visura Camerale — P.IVA 04058890361",
      "created_at":       "2026-02-27 10:05:11"
    }
  ],
  "ricarica_url": "?action=ricarica"
}
Risposta — verifica ordine ricarica (?order_id=N)
{
  "success": true,
  "credito": { "reale": 145.60, "voucher": 0, "totale": 145.60 },
  "ordine": {
    "id":             67,
    "status":         "paid",      // pending | paid | cancelled
    "payment_status": "completed", // pending | completed | failed
    "amount_eur":     100,
    "bonus_amount":   10,
    "total_credit":   110,
    "created_at":     "2026-02-27 09:00:00",
    "paid_at":        "2026-02-27 09:02:33"
  }
}

POST ?action=ricarica

Avvia una ricarica credito con pagamento Revolut. Restituisce una checkout_url a cui reindirizzare l'utente per completare il pagamento con carta/Apple Pay/Google Pay. Richiede PAT.

💡 Per importi ≥ €100 viene applicato automaticamente un bonus del 10%: paghi €100 e ricevi €110 di credito.

Parametri body (JSON)

CampoTipoNote
amountfloatobbligatorio Importo in EUR da pagare (min €10, max €1000)
webhook_urlstringopzionale URL a cui ricevere la conferma quando il pagamento è completato
webhook_secretstringopzionale Segreto HMAC-SHA256 per X-CertiSource-Signature
return_urlstringopzionale URL dove Revolut reindirizza l'utente dopo il pagamento
cURL
curl -X POST "https://certisource.it/atti-service.php?action=ricarica" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "amount": 100,
       "webhook_url": "https://tua-app.it/callbacks/pagamento",
       "webhook_secret": "segreto_hmac",
       "return_url": "https://tua-app.it/grazie"
     }'
JavaScript
const res  = await fetch("https://certisource.it/atti-service.php?action=ricarica", {
  method: "POST",
  headers: { "Authorization": "Bearer cs_pat_...", "Content-Type": "application/json" },
  body: JSON.stringify({ amount: 100, return_url: window.location.origin + "/grazie" })
});
const data = await res.json();

// Reindirizza l'utente al checkout Revolut
if (data.checkout_url) {
  window.location.href = data.checkout_url;
}
Risposta
{
  "success":      true,
  "order_id":     67,
  "amount":       100,           // EUR da pagare
  "bonus_amount": 10,            // EUR bonus (0 se amount < 100)
  "total_credit": 110,           // EUR che verranno aggiunti al saldo
  "status":       "pending",
  "checkout_url": "https://checkout.revolut.com/payment-link/...",
  "stato_url":    "?action=credito&order_id=67",
  "message":      "Pagherai €100 e riceverai €110 di credito (bonus 10% incluso)."
}

Flusso completo

1

POST ?action=ricarica → checkout_url

Ottieni la URL di pagamento Revolut e reindirizza l'utente.

2

L'utente paga su Revolut

Carta, Apple Pay o Google Pay. CertiSource riceve conferma via webhook Revolut.

3

Credito accreditato automaticamente

CertiSource aggiunge il credito (incluso il bonus se applicabile). Il webhook_url che hai fornito riceve un POST con l'evento credit.recharged.

Webhook credit.recharged
{
  "event":        "credit.recharged",
  "order_id":     67,
  "amount_paid":  100,
  "credit_added": 110,
  "bonus_amount": 10,
  "paid_at":      "2026-02-27T09:02:33+01:00"
}
4

Verifica il saldo aggiornato

GET ?action=credito&order_id=67

Oppure polling su ?action=credito per vedere il saldo aggiornato.


Codici di errore

HTTPcodeCausaSoluzione
202 PROCESSING Documento ancora in elaborazione (solo action=dati) Attendi il completamento (action=stato) prima di richiedere i dati
400 BAD_REQUEST Parametro mancante o azione non riconosciuta Controlla i campi obbligatori
401 UNAUTHORIZED Token mancante, scaduto o non valido Rinnova il JWT o verifica il PAT
402 INSUFFICIENT_CREDIT Credito account insufficiente Ricarica il credito su CertiSource
403 FORBIDDEN Richiesta appartenente ad altro utente Usa l'account proprietario della richiesta
404 NOT_FOUND ID richiesta non esistente Verifica l'id restituito da richiesta
409 NOT_READY Scarica chiamata ma documento non ancora pronto Attendi completed: true in stato
422 VALIDATION Parametri non validi (tipo doc errato, VAT formato errato) Controlla la documentazione del campo
429 RATE_LIMIT Troppe richieste Aggiungi un backoff esponenziale
502 PROVIDER_ERROR Errore del provider upstream (OpenAPI.it) Riprova dopo 30s; il credito non viene addebitato

Formato errore

JSON
{
  "success":      false,
  "error":        "Credito insufficiente",
  "code":         "INSUFFICIENT_CREDIT",
  "needed":       4.95,
  "available":    2.10,
  "recharge_url": "https://certisource.it/pricing.html"
}

Informative — REST API v2.0

API REST per creare, avviare e monitorare Informative (indagini documentali a cascata): il sistema acquisisce automaticamente visure, dossier societari, cariche, partecipazioni e altri documenti per il soggetto richiesto, costruendo un grafo di relazioni aziendali.

BASE URL https://certisource.it/api/investigations

Come funziona

1

Crea l'informativa

POST /api/investigations — passa P.IVA (o CF), tipo indagine e opzionalmente webhook_url. Ricevi subito l'investigation_id.

2

Avvia l'elaborazione

POST /api/investigations/{id}/start — CertiSource acquisisce tutti i documenti del piano in background (~30–120s). Il credito viene addebitato automaticamente.

3

Ricevi il webhook o fai polling

Se hai passato webhook_url, ricevi una POST al completamento. Altrimenti fai polling su GET /api/investigations/{id} finché status === "completed".

4

Leggi i dossier

GET /api/investigations/{id}/dossiers — ottieni il grafo completo: azienda target, persone fisiche collegate, aziende correlate, con documenti e dati strutturati per ciascun nodo.

💡 Autenticazione identica agli endpoint Atti Service: PAT (Authorization: Bearer cs_pat_...) oppure JWT B2B. I costi scalano dal credito account.

POST /api/investigations — Crea Informativa

Crea una nuova informativa in stato draft. Non avvia ancora l'elaborazione né addebita credito.

POST https://certisource.it/api/investigations

Body (JSON)

CampoTipoDescrizione
target_typestringrichiesto "company" o "person"
target_vat_numberstringrichiesto se company Partita IVA (11 cifre) o Codice Fiscale azienda
target_fiscal_codestringrichiesto se person Codice fiscale persona fisica (16 car.)
target_namestringopzionale Nome/ragione sociale (migliora matching)
investigation_typestringopzionale "company_full" (default) · "company_basic" · "person_full" · "network_analysis"
webhook_urlstringopzionale URL HTTPS per notifica push al completamento (evita polling)
webhook_secretstringopzionale Segreto HMAC per verificare firma X-CertiSource-Signature
cURL
curl -X POST "https://certisource.it/api/investigations" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "target_type": "company",
       "target_vat_number": "04223920366",
       "investigation_type": "company_full",
       "webhook_url": "https://tua-app.it/webhook/certisource",
       "webhook_secret": "segreto_condiviso"
     }'

Risposta 201

JSON
{
  "success": true,
  "investigation_id": 42,
  "investigation_code": "INV-20260317-A3F9BC",
  "estimated_cost": 6.45,
  "requires_delegations": false
}

POST /api/investigations/{id}/start — Avvia

Avvia l'elaborazione dell'informativa. CertiSource acquisisce in background tutti i documenti del piano, costruisce il grafo societario e indicizza i dati. Il credito viene addebitato progressivamente.

POST https://certisource.it/api/investigations/{id}/start

Body vuoto — nessun parametro richiesto.

cURL
curl -X POST "https://certisource.it/api/investigations/42/start" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN"

Risposta 200

JSON
{
  "success": true,
  "status": "processing",
  "message": "Informativa avviata"
}
Il completamento richiede tipicamente 30–120 secondi per company_full. L'informativa passa da processing a completed (o partial se alcuni documenti falliscono). Usa il webhook o il polling su GET /{id} per sapere quando è pronta.

GET /api/investigations/{id} — Stato

Legge lo stato corrente dell'informativa con contatori e costo effettivo. Usa questo endpoint per il polling se non hai configurato un webhook.

GET https://certisource.it/api/investigations/{id}
JSON — risposta completata
{
  "id": 42,
  "investigation_code": "INV-20260317-A3F9BC",
  "status": "completed",          // draft|pending|processing|completed|partial|failed
  "target_type": "company",
  "target_vat_number": "04223920366",
  "target_name": "RALLY S.R.L.",
  "total_dossiers": 5,
  "total_documents": 7,
  "total_persons_found": 3,
  "total_companies_found": 2,
  "estimated_cost_eur": "6.45",
  "actual_cost_eur": "5.94",
  "started_at": "2026-03-17T14:20:00",
  "completed_at": "2026-03-17T14:21:43"
}

Stati possibili

StatusSignificato
draftCreata, non ancora avviata
processingIn elaborazione — non leggere i dossier ancora
completed✅ Completata, tutti i dossier disponibili
partial⚠️ Completata parzialmente (alcuni documenti falliti)
failed❌ Fallita — nessun addebito ulteriore

GET /api/investigations/{id}/dossiers — Dossier

Restituisce il grafo completo: ogni nodo (azienda o persona) ha i propri documenti acquisiti, dati strutturati e servizi disponibili per acquisto aggiuntivo.

GET https://certisource.it/api/investigations/{id}/dossiers
JSON — struttura risposta
{
  "dossiers": [
    {
      "id": 101,
      "entity_type": "company",          // "company" | "person"
      "is_root": true,
      "vat_number": "04223920366",
      "business_name": "RALLY S.R.L.",
      "documents_completed": 3,
      "documents": [
        {
          "document_type_code": "visura_camerale",
          "status": "completed",
          "file_url": "https://certisource.it/uploads/documents/visura_..."
        }
      ],
      "structured_data": {             // dati parsati dalla visura
        "ragione_sociale": "RALLY S.R.L.",
        "forma_giuridica": "SOCIETA' A RESPONSABILITA' LIMITATA",
        "codice_fiscale": "04223920366",
        "pec": "rally@pec.it",
        "capitale_sociale": "10000",
        "ateco_codes": ["..."],
        "cariche": ["..."],
        "sedi": ["..."]
      }
    },
    {
      "id": 102,
      "entity_type": "person",
      "is_root": false,
      "person_cf": "TRTRMH89S21Z129V",
      "first_name": "RADU",
      "last_name": "MIHAI TRUTA",
      "role_type": "Amministratore Unico",
      "service_data": {               // dati da servizi acquistati
        "current_positions": { "..." },
        "person_report": { "..." }   // se acquistato
      }
    }
  ]
}
📊 I dossier di tipo company includono structured_data con tutti i campi parsati dalla visura (cariche, soci, ATECO, sedi, capitale sociale, addetti, ecc.). Vedi la sezione Dati strutturati per la struttura completa.

POST /api/investigations/{id}/dossiers/{dossierId}/services — Acquista Servizio

Acquista un servizio aggiuntivo per un nodo del grafo. Il credito viene addebitato immediatamente. I servizi asincroni (es. person_report) vengono elaborati in background e notificati via webhook.

POST https://certisource.it/api/investigations/{id}/dossiers/{dossierId}/services
CampoTipoDescrizione
service_codestringrichiesto Codice servizio (vedi tabella sotto)

Servizi disponibili per persone fisiche

CodiceDescrizioneCostoTipo
person_reportReport completo: cariche storiche, immobili, protesti, score affidabilità PIN€ 1,50ASYNC
current_positionsCariche attuali in altre aziende€ 0,50SYNC
ricerca_immobiliRicerca immobili intestati (catasto nazionale)€ 0,50ASYNC
pra_proprietariVeicoli intestati (PRA proprietari)€ 29,90ASYNC ~5gg
ispezione_ipotecariaIspezione ipotecaria nazionale€ 5,90ASYNC

Servizi disponibili per aziende

CodiceDescrizioneCostoTipo
visura_cameraleVisura camerale ordinaria aggiornata€ 4,95ASYNC
visura_storicaVisura storica completa dalla fondazione€ 5,90ASYNC
dossier_societaDossier societario completo (full company report)€ 0,89SYNC
protestiRicerca protesti€ 0,50SYNC
credit_scoringScore di credito aziendale€ 0,50SYNC
cURL — acquisto person_report
curl -X POST "https://certisource.it/api/investigations/42/dossiers/102/services" \
     -H "Authorization: Bearer cs_pat_IL_TUO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "service_code": "person_report" }'

Risposta — servizio asincrono (person_report)

JSON
{
  "success": true,
  "async": true,                  // elaborazione in background (~1h)
  "new_credit_balance": 98.35
}

Quando il servizio asincrono è completato, CertiSource aggiorna i service_data del dossier. Se l'informativa aveva un webhook_url, riceverai una notifica push.


Webhook Informative

Se passi webhook_url alla creazione dell'informativa, CertiSource invia una POST al completamento — sia per il completamento dell'intera informativa, sia per i servizi aggiuntivi asincroni acquistati successivamente.

Evento: informativa completata

Header
POST https://tua-app.it/webhook/certisource
Content-Type: application/json
X-CertiSource-Event: investigation.completed
X-CertiSource-Signature: sha256=<hmac-sha256-del-body>
X-CertiSource-Attempt: 1
Payload JSON
{
  "event":              "investigation.completed",
  "investigation_id":   42,
  "investigation_code": "INV-20260317-A3F9BC",
  "status":             "completed",
  "target_type":        "company",
  "target_vat_number":  "04223920366",
  "target_name":        "RALLY S.R.L.",
  "completed_at":       "2026-03-17T14:21:43",
  "actual_cost_eur":    "5.94",
  "result_url":         "https://certisource.it/api/investigations/42",
  "dossiers_url":       "https://certisource.it/api/investigations/42/dossiers"
}

Verifica firma HMAC

PHP
$secret  = 'segreto_condiviso';
$payload = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_X_CERTISOURCE_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $sig)) {
    http_response_code(403); exit;
}

$data = json_decode($payload, true);
// $data['investigation_id'] → id informativa
// Chiama $data['dossiers_url'] per leggere il grafo
💡
Retry automatico: max 3 tentativi. Il tuo endpoint deve rispondere 2xx entro 10 secondi.
Idempotenza: usa investigation_id per deduplicare (possibili retry).
Fallback: se non ricevi il webhook puoi sempre fare polling su GET /api/investigations/{id}.