3 API3.1
Essai à blanc : POST /validate
Exécute les mêmes contrôles que la soumission. Ne stocke rien et n’envoie rien.
- Dans le bac à sable
En termes simples
Un essai à blanc contrôle une facture comme le ferait une vraie soumission, sans rien envoyer : un intégrateur peut ainsi trouver chaque problème avant que la facture d’un client ne parte où que ce soit.
POST /validate exécute les mêmes contrôles qu’une soumission et répond par un rapport. Il ne met rien en file d’attente et n’envoie rien. L’appel nécessite une clé dotée de la portée submit. Le corps est en JSON, jusqu’à 5 Mo.
Conseil
Commencez ici quand vous mappez un nouveau client.
La requête
| Champ | Signification |
|---|---|
invoice_ref | Obligatoire. L’identifiant du document propre à l’ERP, jusqu’à 100 caractères. Repris dans chaque événement de statut. |
route | Obligatoire. DE-XRECHNUNG, PEPPOL, FR-PA, PL-KSEF ou RO-EFACTURA. |
document | Une facture dans le modèle canonique. Envoyez ce champ ou un export, pas les deux. |
export, connector | Un export d’ERP tel que l’ERP l’a écrit, avec connector à business-central ou sap-b1. Envoyez ce champ ou un document, pas les deux. Pour un canal autre que l’Allemagne, le rapport commence par un niveau mapping. mapping_version choisit une autre version que la version en vigueur (voir versions du mapping). |
formats | Les documents à produire quand le canal laisse le choix. Allemagne : un ou plusieurs parmi xrechnung-ubl (par défaut), xrechnung-cii et zugferd ; KoSIT s’exécute une fois par format, et le PDF passe aussi par Mustang et veraPDF. France : ubl, cii ou facturx. |
environment | Facultatif. Il doit correspondre à l’environnement de votre clé. |
erp_totals | Les totaux calculés par votre ERP : payable_amount, currency et éventuellement tax_amount, sous forme de chaînes. S’ils diffèrent des totaux que le service calcule à partir des lignes, le rapport contient un constat EI-TOTALS-MISMATCH sur erp_totals.payable_amount. |
La liste complète des champs figure dans la référence.
Une facture qui passe
La requête d’exemple est validate-de-ok.json, une facture allemande fictive. Le rapport contient valid: true, un document avec son SHA-256, et quatre niveaux réussis.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @validate-de-ok.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/validate"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("validate-de-ok.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/validate', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('validate-de-ok.json'),
});
console.log(response.status);
console.log(await response.text());{
"valid": true,
"route": "DE-XRECHNUNG",
"documents": [
{
"sha256": "e21ab9d5097022bea30bfa9f9fe0a4c7ca9afdbd672b0ce2f9150461db773625",
"kind": "xrechnung-ubl",
"content_base64": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgi... (7,092 characters, shortened for these docs)"
}
],
"layers": [
{
"findings": [],
"passed": true,
"layer": "schema"
},
{
"findings": [],
"passed": true,
"layer": "mapping"
},
{
"findings": [],
"passed": true,
"layer": "pre-check"
},
{
"findings": [],
"passed": true,
"layer": "kosit"
}
]
}Une facture qui échoue
La même facture sans le nom du vendeur, validate-de-missing-seller-name.json. Un essai à blanc répond quand même 200. valid vaut false, et chaque constat nomme le champ et indique qui le corrige. Les deux lignes surlignées sont celles à regarder : le code du catalogue, et le field dans les données du client.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @validate-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/validate"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("validate-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/validate', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('validate-de-missing-seller-name.json'),
});
console.log(response.status);
console.log(await response.text());{
"valid": false,
"route": "DE-XRECHNUNG",
"layers": [
{
"findings": [
{
"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."
}
],
"passed": false,
"layer": "schema"
},
{
"findings": [],
"passed": true,
"layer": "mapping"
},
{
"findings": [
{
"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."
}
],
"passed": false,
"layer": "pre-check"
}
]
}Lire le rapport
validvauttrueseulement si tous les niveaux ont réussi.- Les
layerss’exécutent dans l’ordre :schema,mapping,pre-check, puis les validateurs propres au canal, nommés d’après ce qui s’est exécuté (par exempleKoSIT-XRechnung-3.0.2). - Chaque constat comporte un
codedu catalogue, lefield,who_fixes(usouerp), unfix_hint, unmessageet le niveausource. La page Erreurs et catalogue liste tous les codes. documentsliste ce qui a été produit, avec son SHA-256 et, pour un fichier de 2 MiB ou moins, ses octets danscontent_base64. Il est présent quand la facture est valide.
Autres réponses
| Statut | Signification |
|---|---|
200 | Un rapport, valide ou non. |
400 | La requête est illisible : pas du JSON, un champ erroné, ou document et export présents tous les deux. |
401 | Aucune clé, ou une clé inconnue. |
403 | La clé n’a pas la portée submit. |
413 | Le corps dépasse 5 Mo (payload-too-large). |
422 | Un nombre hors des limites, par exemple de plus de 40 caractères (EI-SCHEMA, avec le nom du champ). Tous les autres constats figurent dans le rapport en 200. |
429 | Trop de requêtes pour la clé. Attendez Retry-After secondes. |
503 | Tous les validateurs sont occupés (busy). Réessayez après Retry-After secondes. |
curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @not-json.txtimport 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/validate"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("not-json.txt")))
.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/validate', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('not-json.txt'),
});
console.log(response.status);
console.log(await response.text());{
"detail": "The request is not valid JSON.",
"type": "https://eurinvoice.com/problems/bad-request",
"title": "The request could not be read",
"status": 400
}