Prima di iniziare
- Crea un account e conferma l'e-mail: hai 30 giorni e 20 fatture di prova.
- Usa le stesse credenziali del portale per il login alle API (
POST https://api.fattura-elettronica.cloud/api/login) e conserva il token. - Per ricevere fatture e notifiche sul tuo server, imposta gli indirizzi dei tuoi endpoint nel profilo del portale e attiva l'inoltro.
Inviare una fattura elettronica in Python
L'esempio usa requests; con httpx il codice è quasi identico. Il file va passato in files= così requests costruisce il multipart. La risposta contiene inoltro.id_sdi, l'identificativo con cui arriveranno le notifiche della fattura.
# Python 3.8+ (pip install requests)
import requests
API = "https://api.fattura-elettronica.cloud/api"
HEADERS = {"Accept": "application/json"}
# 1. Login: il token vale a lungo, salvalo e rifai il login solo se ricevi 401
r = requests.post(f"{API}/login", headers=HEADERS, timeout=30,
data={"email": "tua@email.it", "password": "LaTuaPassword"})
token = r.json().get("access_token")
if not token:
raise SystemExit(f"Login non riuscito: {r.json().get('message')}")
auth = {**HEADERS, "Authorization": f"Bearer {token}"}
# 2. Invio della fattura XML allo SdI
nome = "IT01234567890_00001.xml"
with open(nome, "rb") as f:
r = requests.post(f"{API}/inoltro", headers=auth, timeout=90,
files={"fileFattura": (nome, f, "application/xml")})
dati = r.json()
if r.status_code == 200 and dati.get("esito") == "ok":
print("Inviata, identificativo SdI:", dati["inoltro"]["id_sdi"])
else:
print(f"Errore {r.status_code}: {dati.get('messaggio')}")
# 3. Elenco delle fatture inviate (esito: 0 in attesa, 1 consegnata, 2 mancata consegna, 3 scartata, ...)
for inoltro in requests.get(f"{API}/inoltro", headers=auth, timeout=30).json()["inoltri"]:
print(inoltro["id_sdi"], inoltro["esito"])
Codici di risposta: 200 inviata, 400 file non valido, 401 token scaduto, 403 pacchetto esaurito o e-mail non confermata, 422 rifiutata dallo SdI. Tutti i dettagli nella documentazione completa.
Ricevere le fatture elettroniche in Python
Quando arriva una fattura per il tuo codice destinatario la mandiamo al tuo endpoint con una POST form-urlencoded: strxml (XML della fattura), xmlF (file originale), xmlM (metadati SdI), tutti in base64, e formato. Per gli endpoint l'esempio usa Flask, ma vale lo stesso per FastAPI o Django: leggi il corpo grezzo (request.get_data(), await request.body(), request.body) prima di decodificare i campi, perché la firma si calcola su quello.
# Python 3.8+ con Flask (pip install flask) - URL da indicare in "Endpoint ricezione fatture"
import base64, hashlib, hmac, os, time
import xml.etree.ElementTree as ET
from urllib.parse import parse_qs
from flask import Flask, request
SEGRETO = os.environ.get("FE_SEGRETO", "") # pagina Integrazione del portale; vuoto se non usi la firma
app = Flask(__name__)
def firma_valida(corpo: bytes) -> bool:
"""X-FE-Firma = HMAC-SHA256 di "timestamp\\ncorpo" con il tuo segreto"""
if not SEGRETO:
return True
ts = request.headers.get("X-FE-Timestamp", "")
attesa = hmac.new(SEGRETO.encode(), ts.encode() + b"\n" + corpo, hashlib.sha256).hexdigest()
return (ts.isdigit() and abs(time.time() - int(ts)) <= 300
and hmac.compare_digest(attesa, request.headers.get("X-FE-Firma", "")))
@app.post("/ricevi-fatture")
def ricevi_fatture():
corpo = request.get_data() # il corpo così com'è arrivato, serve per la firma
if not firma_valida(corpo):
return "firma non valida", 403
dati = {k: v[0] for k, v in parse_qs(corpo.decode()).items()}
xml_fattura = base64.b64decode(dati.get("strxml", "")) # XML della fattura, già estratto dal .p7m
file_originale = base64.b64decode(dati.get("xmlF", "")) # file arrivato dallo SdI (.xml o .xml.p7m)
metadati = ET.fromstring(base64.b64decode(dati.get("xmlM", "")))
id_sdi = metadati.findtext("{*}IdentificativoSdI")
if not id_sdi or not xml_fattura:
return "dati mancanti", 400
# Stesso IdentificativoSdI = stessa fattura: se arriva di nuovo sovrascrivi, non duplicare
os.makedirs("fatture_ricevute", exist_ok=True)
with open(f"fatture_ricevute/{id_sdi}.xml", "wb") as f:
f.write(xml_fattura)
return "OK" # rispondi esattamente OK: con qualsiasi altra risposta la fattura viene rimandata
Rispondi OK con HTTP 200 solo dopo aver salvato la fattura. Se il tuo server non risponde riproviamo per circa 4 giorni: la stessa fattura può arrivare più volte, riconoscila da IdentificativoSdI.
Ricevere le notifiche SdI in Python
Ogni notifica dello SdI (consegna, scarto, mancata consegna, esiti della PA) arriva al tuo endpoint notifiche con i campi tipoNotifica e esitojson. Il significato di ogni notifica è spiegato nella guida alle notifiche SdI.
# Flask - URL da indicare in "Endpoint ricezione notifiche" (firma_valida è la stessa dell'endpoint fatture)
import json
from urllib.parse import parse_qs
TIPI = {1: "RC", 2: "MC", 3: "NS", 4: "NE", 5: "DT", 6: "AT"}
STATI = {
"RC": "consegnata",
"MC": "mancata consegna: disponibile nel cassetto fiscale del cliente",
"NS": "scartata", # i motivi sono in notifica["ListaErrori"]
"NE": "esito del committente", # solo PA: EsitoCommittente.Esito = EC01 accettata, EC02 rifiutata
"DT": "decorrenza termini", # solo PA: nessuna risposta entro 15 giorni
"AT": "attestazione di avvenuta trasmissione",
}
@app.post("/ricevi-notifiche")
def ricevi_notifiche():
corpo = request.get_data()
if not firma_valida(corpo):
return "firma non valida", 403
dati = {k: v[0] for k, v in parse_qs(corpo.decode()).items()}
tipo = TIPI.get(int(dati.get("tipoNotifica", 0)))
notifica = json.loads(dati.get("esitojson", "null")) # la notifica dello SdI convertita in JSON
if not tipo or not notifica:
return "dati mancanti", 400
# IdentificativoSdI è lo stesso restituito dall'API all'invio (inoltro.id_sdi).
# La stessa notifica può arrivare due volte: l'aggiornamento deve poter essere ripetuto.
aggiorna_fattura(id_sdi=notifica["IdentificativoSdI"], stato=STATI[tipo], dettaglio=notifica)
return "OK" # rispondi esattamente OK: con qualsiasi altra risposta la notifica viene rimandata
Prossimi passi
- Genera il segreto personale nella pagina Integrazione del portale e attiva la verifica della firma.
- Controlla che i file rispettino il formato XML e le regole del nome file.
- Gestisci gli scarti: codici di errore e reinvio.
Inizia con Python
Prova gratis 30 giorni con 20 fatture, poi da 5 € al mese.