3 API3.2
Inviare JSON: POST /invoices
JSON canonico o un export dell’ERP.
- Nella sandbox
In parole semplici
Questa chiamata invia una fattura: se supera tutti i controlli il servizio la mette in coda, altrimenti la risposta indica il campo che non li ha superati e non viene messo in coda nulla.
POST /invoices controlla la fattura come una simulazione. Se la fattura supera i controlli, il servizio la mette in coda e risponde 202 con un id. Se un controllo non viene superato, la risposta è 422 e non viene messo in coda nulla. La chiamata richiede una chiave con lo scope submit e una Idempotency-Key.
La richiesta
| Parte | Significato |
|---|---|
Intestazione Idempotency-Key | Obbligatoria, da 8 a 100 caratteri. |
invoice_ref | Obbligatorio. L’id del documento nell’ERP, fino a 100 caratteri. |
route | Facoltativo. Uno dei cinque canali. Se manca, il servizio lo sceglie in base alla fattura (i paesi del venditore e dell’acquirente, il profilo e le credenziali memorizzate del cliente). Se nessuna regola si applica, la risposta è 422, EI-ROUTE-UNDECIDED o EI-ROUTE-PEPPOL-UNKNOWN. |
document | Una fattura nel modello canonico. Inviare questo oppure un export con un connector, non entrambi. |
export, connector | Un export dell’ERP così come l’ERP lo ha scritto, con connector impostato a business-central o sap-b1. Il servizio lo mappa con la versione di mappatura attiva del connettore, o con la mapping_version indicata (vedere versioni di mappatura). Un rilievo nella mappatura risponde 422 e indica il campo dell’ERP. L’export viene conservato insieme alla fattura. |
formats | Facoltativo. Indicarne al massimo uno: il documento che viene costruito, controllato e inviato. |
environment | Facoltativo. Se viene inviato, deve corrispondere all’ambiente della chiave, altrimenti la risposta è 400. |
client | Facoltativo. La chiave propria di un cliente può ometterlo o indicare il proprio cliente; qualsiasi altro cliente riceve 403. I limiti delle infrastrutture si contano per cliente (vedere limiti). |
Il corpo è limitato a 5 MB. In produzione un export viene letto solo se le impostazioni del connettore del cliente contengono i dati di venditore e di pagamento propri del cliente; senza di essi la risposta è 422, connector-settings-missing, e indica cosa manca. La sandbox lo legge con i dati di esempio del connettore.
Una fattura accettata
L’esempio è submit-de.json. Location contiene l’URL della fattura, e links punta alla fattura e ai suoi eventi. Lo stato è queued: non è stato inviato nulla.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0001" \
--data-binary @submit-de.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/invoices"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.header("Idempotency-Key", "order-2026-0001")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("submit-de.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/invoices', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-2026-0001',
},
body: await readFile('submit-de.json'),
});
console.log(response.status);
console.log(await response.text());{
"links": {
"self": "/invoices/inv_936a93e38de84e7b0a1d7681",
"events": "/invoices/inv_936a93e38de84e7b0a1d7681/events"
},
"id": "inv_936a93e38de84e7b0a1d7681",
"state": "queued"
}Una fattura che non supera un controllo
La stessa chiamata senza il nome del venditore, submit-de-missing-seller-name.json. La risposta è un documento problem JSON con un elenco errors. Ogni errore è una segnalazione come quelle di un report di simulazione. Le righe evidenziate sono il codice e il campo da correggere.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0002" \
--data-binary @submit-de-missing-seller-name.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/invoices"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.header("Idempotency-Key", "order-2026-0002")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("submit-de-missing-seller-name.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/invoices', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-2026-0002',
},
body: await readFile('submit-de-missing-seller-name.json'),
});
console.log(response.status);
console.log(await response.text());{
"type": "https://eurinvoice.com/problems/validation-failed",
"title": "The invoice did not pass the checks",
"errors": [
{
"code": "EI-SCHEMA",
"field": "seller.name",
"related": [
{
"code": "Art.226(5)",
"source": "pre-check"
}
],
"fix_hint": "Read the JSON path in the error and correct the client mapping.",
"who_fixes": "us",
"source": "schema",
"message": "We could not read this invoice from your export. We are correcting our mapping; if a field is missing in the ERP we will tell you which one."
},
{
"code": "Art.226(5)",
"field": "seller.name",
"related": [
{
"code": "EI-SCHEMA",
"source": "schema"
}
],
"fix_hint": "Complete the party's name and address in the master data.",
"who_fixes": "erp",
"source": "pre-check",
"message": "A company name or street address is missing. Complete the company or customer record in the ERP."
}
],
"status": 422
}Idempotenza
- La stessa chiave con lo stesso corpo restituisce di nuovo la prima risposta, con lo stesso id.
- La stessa chiave con un corpo diverso dà
409. - Viene memorizzata anche una risposta
422. Inviare una fattura corretta con una nuova chiave. - Una risposta memorizzata viene conservata finché vengono conservati i dati del cliente.
- Una seconda richiesta con una chiave la cui prima richiesta è ancora in corso riceve
409,request-in-progress. Una chiave la cui richiesta non ha mai ricevuto risposta, perché il server si è fermato, viene liberata dopo 15 minuti.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0001" \
--data-binary @submit-de.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/invoices"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.header("Idempotency-Key", "order-2026-0001")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("submit-de.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/invoices', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-2026-0001',
},
body: await readFile('submit-de.json'),
});
console.log(response.status);
console.log(await response.text());{
"links": {
"self": "/invoices/inv_936a93e38de84e7b0a1d7681",
"events": "/invoices/inv_936a93e38de84e7b0a1d7681/events"
},
"id": "inv_936a93e38de84e7b0a1d7681",
"state": "queued"
}curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0001" \
--data-binary @submit-de-changed.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/invoices"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.header("Idempotency-Key", "order-2026-0001")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("submit-de-changed.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/invoices', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-2026-0001',
},
body: await readFile('submit-de-changed.json'),
});
console.log(response.status);
console.log(await response.text());{
"type": "https://eurinvoice.com/problems/idempotency-conflict",
"title": "This Idempotency-Key was already used with a different body",
"status": 409
}Un documento già inviato
Un documento identico a uno attivo, per lo stesso cliente e lo stesso canale, viene accettato di nuovo con una nuova chiave. La risposta riporta duplicate_of, l’id della prima fattura, e non viene messo in coda nulla di nuovo.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0003" \
--data-binary @submit-de.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/invoices"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.header("Idempotency-Key", "order-2026-0003")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("submit-de.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/invoices', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-2026-0003',
},
body: await readFile('submit-de.json'),
});
console.log(response.status);
console.log(await response.text());{
"links": {
"self": "/invoices/inv_936a93e38de84e7b0a1d7681",
"events": "/invoices/inv_936a93e38de84e7b0a1d7681/events"
},
"id": "inv_936a93e38de84e7b0a1d7681",
"state": "queued",
"duplicate_of": "inv_936a93e38de84e7b0a1d7681"
}Risposte
| Stato | Significato |
|---|---|
202 | Accettata e messa in coda, oppure duplicato di una fattura attiva. |
400 | Il corpo non è JSON valido, un campo è errato, oppure la Idempotency-Key manca o non è lunga da 8 a 100 caratteri. |
401 | Nessuna chiave, o una chiave sconosciuta. |
403 | La chiave non ha lo scope submit, oppure indica un altro cliente (forbidden). |
409 | La chiave è stata usata con un corpo diverso (idempotency-conflict), oppure la sua prima richiesta è ancora in corso (request-in-progress). |
413 | Il corpo supera 5 MB (payload-too-large). |
422 | La fattura non ha superato un controllo. errors indica cosa non va e chi lo corregge. |
429 | Troppe richieste per la chiave. Attendere Retry-After secondi. |
503 | Un’altra richiesta per lo stesso documento è ancora in fase di archiviazione (busy). Non è stato scritto nulla e la chiave può essere riutilizzata. Riprovare dopo Retry-After secondi. |
Avvertenza
Dopo un 202 non si reinvia mai. Gli errori lato infrastruttura che un nuovo tentativo può superare vengono ritentati secondo il nostro calendario, e uno scarto definitivo va nell’elenco degli scarti.