3 API3.2
Transmitere JSON: POST /invoices
JSON canonic sau un export ERP.
- În sandbox
În cuvinte simple
Acest apel transmite o factură: dacă trece toate verificările, serviciul o pune în coadă, iar dacă nu, răspunsul indică câmpul care nu a trecut și nu se pune nimic în coadă.
POST /invoices verifică factura ca o rulare de probă. Dacă trece, serviciul o pune în coadă și răspunde 202 cu un ID. Dacă o verificare eșuează, răspunsul este 422 și nu se pune nimic în coadă. Apelul necesită o cheie cu domeniul submit și un Idempotency-Key.
Cererea
| Element | Semnificație |
|---|---|
antetul Idempotency-Key | Obligatoriu, între 8 și 100 de caractere. |
invoice_ref | Obligatoriu. ID-ul propriu al documentului în ERP, de cel mult 100 de caractere. |
route | Opțional. Unul dintre cele cinci canale. Dacă lipsește, serviciul îl alege după factură (țările vânzătorului și ale cumpărătorului, profilul și datele de autentificare stocate ale clientului). Dacă nu se potrivește nicio regulă, răspunsul este 422, EI-ROUTE-UNDECIDED sau EI-ROUTE-PEPPOL-UNKNOWN. |
document | O factură în modelul canonic. Trimiteți acest câmp sau un export cu un connector, nu pe amândouă. |
export, connector | Un export ERP așa cum l-a scris ERP-ul, cu connector setat pe business-central sau sap-b1. Serviciul îl mapează cu versiunea de mapare activă a conectorului sau cu mapping_version indicat de dumneavoastră (consultați versiunile de mapare). O constatare în mapare primește 422 și indică câmpul ERP. Exportul este păstrat împreună cu factura. |
formats | Opțional. Indicați cel mult unul: documentul care este generat, verificat și trimis. |
environment | Opțional. Dacă îl trimiteți, trebuie să corespundă mediului cheii dumneavoastră, altfel răspunsul este 400. |
client | Opțional. Cheia proprie a unui client îl poate omite sau poate indica propriul client; orice alt client primește 403. Plafoanele rețelelor se contorizează pentru fiecare client (consultați limitele). |
Corpul cererii este limitat la 5 MB. În producție, un export este citit doar dacă setările conectorului clientului conțin datele proprii ale vânzătorului și de plată ale clientului; fără ele, răspunsul este 422, connector-settings-missing, cu indicarea a ceea ce lipsește. Sandboxul îl citește cu datele exemplu ale conectorului.
O factură acceptată
Exemplul este submit-de.json. Location conține URL-ul facturii, iar links trimite la factură și la evenimentele sale. Starea este queued: nu s-a trimis nimic.
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"
}O factură care nu trece o verificare
Același apel, cu numele vânzătorului eliminat, submit-de-missing-seller-name.json. Răspunsul este un document problem JSON cu o listă errors. Fiecare eroare este o constatare ca cele dintr-un raport de rulare de probă. Liniile evidențiate sunt codul și câmpul de corectat.
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
}Idempotență
- Aceeași cheie cu același corp returnează din nou primul răspuns, cu același ID.
- Aceeași cheie cu un corp diferit primește
409. - Răspunsurile stocate includ și
422. Trimiteți factura corectată cu o cheie nouă. - Un răspuns stocat este păstrat cât timp sunt păstrate datele clientului.
- O a doua cerere cu o cheie a cărei primă cerere încă rulează primește
409,request-in-progress. O cheie a cărei cerere nu a primit niciodată răspuns, pentru că serverul s-a oprit, este eliberată după 15 minute.
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 document trimis anterior
Un document identic cu unul activ, pentru același client și același canal, este acceptat din nou sub o cheie nouă. Răspunsul conține duplicate_of, ID-ul primei facturi, și nu se pune nimic nou în coadă.
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"
}Răspunsuri
| Cod | Semnificație |
|---|---|
202 | Acceptată și pusă în coadă sau duplicat al unei facturi active. |
400 | Corpul nu este JSON valid, un câmp este greșit sau Idempotency-Key lipsește ori nu are între 8 și 100 de caractere. |
401 | Lipsește cheia sau cheia este necunoscută. |
403 | Cheia nu are domeniul submit sau indică un alt client (forbidden). |
409 | Cheia a fost folosită cu un corp diferit (idempotency-conflict) sau prima ei cerere încă rulează (request-in-progress). |
413 | Corpul cererii depășește 5 MB (payload-too-large). |
422 | Factura nu a trecut o verificare. errors arată ce nu a trecut și cine corectează. |
429 | Prea multe cereri pentru cheie. Așteptați numărul de secunde din Retry-After. |
503 | O altă cerere pentru același document este încă în curs de stocare (busy). Nu s-a scris nimic, iar cheia poate fi folosită din nou. Reîncercați după numărul de secunde din Retry-After. |
Atenție
După un 202 nu retrimiteți niciodată. Erorile din partea rețelei care pot trece la o nouă încercare sunt reîncercate după programul nostru, iar o respingere definitivă ajunge pe lista de gestionare.