Credenziali per le infrastrutture
Archiviare e sostituire le credenziali di un cliente per le infrastrutture.
- Nella sandboxarchiviazione
- Non ancora nella sandboxinfrastrutture
In parole semplici
Una credenziale per un’infrastruttura è la chiave o l’autorizzazione che ci permette di operare presso la rete o l’autorità di un paese per conto di un cliente, per esempio un token per il sistema nazionale di fatturazione elettronica della Polonia (KSeF). Il cliente la rilascia o la autorizza, e la credenziale entra in eurinvoice tramite l’API, mai via e-mail o chat. La conserviamo cifrata e non la mostriamo mai più. La sandbox ospitata non ne contiene ancora nessuna, quindi un canale lì non invia nulla.
Una credenziale per un’infrastruttura ci permette di operare presso un canale per conto di un cliente: un token KSeF, un’autorizzazione ANAF, una chiave di piattaforma. Entra tramite questa API, mai via e-mail o chat. L’API la conserva e non la restituisce mai. Le chiamate richiedono una chiave con lo scope admin, e la chiave di un cliente archivia credenziali solo per il proprio cliente.
Come viene conservato un valore
Un valore viene cifrato prima di essere archiviato, conservato separatamente dalla chiave che lo protegge, e non viene mai mostrato di nuovo. Una credenziale viene aperta solo quando il servizio deve chiamare il canale di quel cliente. Ogni utilizzo scrive una riga di audit senza valore.
Una credenziale il cui expires_on dista meno di 30 giorni registra un avviso. La revoca cancella il valore e mantiene le date.
Campi
| Campo | Significato |
|---|---|
client | L’identificativo del cliente, lo stesso che riporta una fattura: da 1 a 64 lettere, cifre, punti, trattini bassi o trattini. La chiave propria di un cliente può ometterlo. |
route | DE-XRECHNUNG, PEPPOL, FR-PA, PL-KSEF o RO-EFACTURA. |
kind | Dà un nome alla credenziale e decide come viene letta, fino a 80 caratteri tra lettere, cifre, punti, trattini bassi e trattini: ksef-token per KSeF e anaf-oauth per ANAF. Per un canale Peppol o francese, indichiamo noi il tipo e la forma del valore quando inizia l’avvio in produzione. |
issued_by | Chi l’ha rilasciata, fino a 200 caratteri. |
issued_on | La data di rilascio, YYYY-MM-DD. |
expires_on | Facoltativo, YYYY-MM-DD. Da impostare se la credenziale scade. |
environment | Facoltativo. Deve essere l’ambiente del servizio che si chiama; una sandbox archivia solo credenziali della sandbox. |
legal_entity | Facoltativo, fino a 64 caratteri. L’identificativo del venditore per cui agisce la credenziale: una partita IVA, un NIP, un SIREN o un numero di registro dell’impresa. Una credenziale che non lo ha serve le altre fatture del cliente. |
value | Il segreto, fino a 16.384 caratteri. Obbligatorio e mai restituito. |
Il servizio verifica che un valore possa funzionare per il suo tipo, per esempio che un token KSeF sia un JSON con un nip e un token. Un valore anaf-oauth è un testo JSON con un cui e un access_token, oppure un refresh_token con il client_id e il client_secret dell’applicazione. Un valore che non può funzionare riceve 422, credential-unusable, e la risposta indica quale forma si aspetta senza citare il valore.
Una risposta riporta id (cred_ e 24 caratteri esadecimali), i campi sopra tranne value, stored (true finché è conservato un valore) e revoked_at dopo la revoca.
Un cliente può avere una credenziale attiva per ciascuna combinazione di canale, kind, ambiente ed entità giuridica. Il worker usa la più vecchia non revocata per il cliente e il canale della fattura.
Archiviare una credenziale
POST /credentials accetta i campi sopra come JSON e risponde 201. La richiesta è credential-create.json.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-create.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-create.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-create.json'),
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2027-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Se si archivia di nuovo la stessa credenziale mentre la prima è attiva, la risposta è 409.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-create.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-create.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-create.json'),
});
console.log(response.status);
console.log(await response.text());{
"type": "https://eurinvoice.com/problems/credential-exists",
"title": "This client already has this credential",
"status": 409
}Elencare e leggere
GET /credentials restituisce tutte le credenziali, attive e revocate, come {"data": [...]}. GET /credentials/{id} ne restituisce una, e 404 per un id che non ha.
curl "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>"import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
headers: {
Authorization: 'Bearer <your-api-key>',
},
});
console.log(response.status);
console.log(await response.text());{
"data": [
{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2027-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}
]
}Sostituire
POST /credentials/{id} accetta un nuovo value e, facoltativamente, un nuovo expires_on. Cliente, canale, tipo e data di rilascio restano invariati. Usarla dopo il rinnovo di un token. La richiesta è credential-replace.json.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-replace.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-replace.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-replace.json'),
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2028-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Revocare
POST /credentials/{id}/revoke cancella il valore e imposta revoked_at. Ripeterla non cambia nulla.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke" \
-H "Authorization: Bearer <your-api-key>"import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke"))
.header("Authorization", "Bearer <your-api-key>")
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
},
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"revoked_at": "2026-10-06T19:49:12.987Z",
"kind": "anaf-oauth",
"expires_on": "2028-09-01",
"stored": false,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Avvertenza
Un record revocato resta, per la traccia di audit. Non può essere sostituito: la risposta è 409, credential-revoked. Archiviare invece la nuova credenziale come nuovo record. Una credenziale revocata non impedisce più di archiviare di nuovo la stessa combinazione di cliente, canale e tipo.
Risposte
| Stato | Significato |
|---|---|
200 | L’elenco, una credenziale, una sostituzione o una revoca. |
201 | Archiviata. |
400 | Il corpo non è JSON, un campo obbligatorio manca o è troppo lungo, route non è uno dei cinque canali, oppure una data non è YYYY-MM-DD. |
401 | Nessuna chiave, o una chiave sconosciuta. |
403 | La chiave non ha lo scope admin, oppure indica un altro cliente. |
404 | La credenziale non esiste. |
409 | Il cliente ha già questa credenziale (credential-exists), oppure la credenziale è stata revocata (credential-revoked). |
413 | Il corpo supera 5 MB (payload-too-large). |
422 | Il valore non può funzionare per questo tipo (credential-unusable). |
429 | Troppe richieste per la chiave. Attendere Retry-After secondi. |
503 | Il servizio non ha una chiave master con cui cifrare un valore (unavailable). |
Cosa serve a ciascun canale
Nella sandboxda fonti ufficiali, verificate il 2 ott. 2026| Canale | Cosa fornisce il cliente | Chi la rilascia | Durata |
|---|---|---|---|
PL-KSEF | Un token KSeF che possa inviare fatture (InvoiceWrite) e, per gli stati, leggerle (InvoiceRead). I suoi permessi vengono fissati alla creazione, quindi una modifica richiede un nuovo token. L’accesso con un certificato KSeF non è ancora supportato. Archiviare una credenziale ksef che non sia un ksef-token riceve 422, credential-unusable. | L’amministratore KSeF del cliente. | Vedere la nota qui sotto. |
RO-EFACTURA | Un’autorizzazione OAuth per la nostra applicazione ANAF registrata. | Una persona con diritti SPV per il CUI del cliente: il legale rappresentante o il commercialista. Accede al portale di ANAF con un certificato qualificato. | Il token di accesso dura 90 giorni e il token di aggiornamento 365 giorni. |
PEPPOL | Il consenso a registrare il cliente come partecipante Peppol, e la verifica d’identità richiesta dall’access point. | Il cliente firma il consenso e supera la verifica. La chiave dell’access point è nostra, quindi non c’è alcuna credenziale del cliente da archiviare. | La chiave dell’access point è nostra e la ruotiamo noi. |
FR-PA | L’account intestato al cliente sulla sua Plateforme Agréée e le credenziali API per la società. | Il cliente le crea sulla propria piattaforma, oppure ci concede l’accesso. | Stabilita dalla piattaforma. Chiedere quale tipo rilascia. |
DE-XRECHNUNG | Nulla per la Germania in sé. Via e-mail, la casella di invio. Via Peppol, la registrazione presso l’access point. | Il cliente. | Non applicabile. |
Suggerimento
Impostare expires_on in base alle date indicate sopra, così l’avviso a 30 giorni scatta prima che un token di aggiornamento scada.
Fonti: procedura OAuth di ANAF (durata dei token), documentazione dell’API KSeF: token (permessi fissati alla creazione del token).
Avvertenza
Polonia: le fonti non concordano sulla durata di validità di un token KSeF. Il manuale del Ministero delle Finanze (edizione del 9 feb. 2026) e la pagina dell’applicazione per i contribuenti (modificata il 31 mar. 2026) dicono che i token consentono l’autenticazione fino al 31 dic. 2026. La pagina di domande e risposte del Ministero dice che il Ministero ha deciso di mantenere i token in KSeF 2.0 senza una data di scadenza (risposta 39). Una segnalazione pubblica nel repository dell’API KSeF del Ministero gli chiede di risolvere la discrepanza. Verificato il 2 ott. 2026. Finché la questione non sarà risolta, aspettarsi di dover rinnovare il token se il Ministero fisserà una data di scadenza. L’accesso con un certificato non è ancora supportato, quindi un token è l’unico modo di accedere. Vedere il manuale, le domande e risposte e la segnalazione.