Java code samples for creating, validating, and parsing XRechnung electronic invoices using the InvoiceXML API. Requires Java 15 or later (Java 17 or 21 LTS recommended): Create.java uses text blocks, which were introduced in Java 15. The other examples also compile on Java 8. Runs in Spring Boot, Quarkus, Micronaut, Jakarta EE, Android, AWS Lambda, or plain public static void main console apps.
For background on the XRechnung standard itself (what it is, the Leitweg-ID, legal status), see the main repository README.
Every example in this folder calls the InvoiceXML REST API. Sign up and generate a free API key here:
→ https://www.invoicexml.com/account/authentication
Pass it as a Bearer token on every request:
Authorization: Bearer YOUR_API_KEY
Important: set apiKey in the examples to the raw key only, without the Bearer prefix. If your account page shows the full header value (e.g. Bearer ixml_a1b2c3...), copy only the part after Bearer . The code adds the prefix itself when building the Authorization header.
- Java 15 or later (Java 17 or 21 LTS recommended).
Create.javauses text blocks, a Java 15+ feature; the other four examples also compile on Java 8. - OkHttp 4.x for clean multipart handling and bearer auth
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>implementation 'com.squareup.okhttp3:okhttp:4.12.0'OkHttp is the de facto standard HTTP client for modern Java. Java's built-in java.net.http.HttpClient does not support multipart out of the box, so doing this in pure JDK would triple the line count of every example.
| File | Operation | API endpoint |
|---|---|---|
Create.java |
Build an XRechnung 3.0 XML invoice | POST /v1/create/xrechnung |
Validate.java |
Validate an XRechnung file against the KoSIT rules | POST /v1/validate/xrechnung |
ExtractJson.java |
Parse an XRechnung XML into JSON | POST /v1/extract/json |
AiConvert.java |
(Experimental) Convert a plain PDF to XRechnung with AI | POST /v1/transform/to/xrechnung |
Render.java |
Render XRechnung XML into a human-readable PDF | POST /v1/render/xrechnung/to/pdf |
Each file is a standalone public class with a main method, runnable with javac and java directly, or as part of any Maven or Gradle project.
Note on the snippets below: they are excerpts from those files. The
tryblocks have nocatchclause, so the enclosing method must declarethrows Exception(or at leastthrows IOException), exactly as the full files do withpublic static void main(String[] args) throws Exception. Pasting a snippet into a method without thatthrowsclause will not compile ("unreported exception IOException"). When in doubt, copy the complete file.
String json = """
{
"invoice": {
"invoiceNumber": "XR-2026-001",
"issueDate": "2026-05-18",
"currency": "EUR",
"buyerReference": "991-12345-67",
"seller": {
"name": "Acme GmbH",
"vatIdentifier": "DE123456789",
"legalRegistration": { "identifier": "HRB 12345" },
"postalAddress": { "line1": "Hauptstraße 12", "city": "Berlin", "postCode": "10115", "country": "DE" },
"contact": { "name": "Max Mustermann", "phone": "+49 30 12345678", "email": "billing@acme.de" },
"electronicAddress": { "identifier": "DE123456789", "schemeId": "9930" }
},
"buyer": {
"name": "Bundesamt für Musterverwaltung",
"postalAddress": { "line1": "Behördenstraße 5", "city": "Bonn", "postCode": "53113", "country": "DE" },
"electronicAddress": { "identifier": "991-12345-67", "schemeId": "0204" }
},
"paymentDetails": { "paymentAccountIdentifier": "DE89370400440532013000" },
"lines": [{
"quantity": 10,
"priceDetails": { "netPrice": 150.00 },
"vatInformation": { "rate": 19.00 },
"item": { "name": "Senior consulting" }
}]
},
"options": { "syntax": "ubl" }
}
""";
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url("https://api.invoicexml.com/v1/create/xrechnung")
.header("Authorization", "Bearer " + apiKey)
.post(body)
.build();
try (Response response = new OkHttpClient().newCall(request).execute()) {
String xml = response.body().string();
Files.write(Paths.get("invoice-xrechnung.xml"), xml.getBytes("UTF-8"));
}buyerReference carries the Leitweg-ID (BT-10), and the seller contact and electronicAddress groups are what the XRechnung CIUS requires on top of plain EN 16931. Omit any of them and the API returns a 400 naming the BR-DE-* rule you missed.
The response is the XRechnung 3.0 XML document, validated against the KoSIT rules before delivery.
Full example: Create.java | API reference
RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", "invoice.xml",
RequestBody.create(new File("invoice.xml"), MediaType.parse("application/xml")))
.build();
Request request = new Request.Builder()
.url("https://api.invoicexml.com/v1/validate/xrechnung")
.header("Authorization", "Bearer " + apiKey)
.post(body)
.build();
try (Response response = new OkHttpClient().newCall(request).execute()) {
System.out.println(response.body().string());
}Returns a JSON validation report listing any rule failures (EN 16931 BR-* and BR-CO-, plus the German BR-DE- rules).
Full example: Validate.java | API reference
Useful for feeding XRechnung invoices into Spring Boot services, message queues, or any pipeline that prefers JSON over XML.
RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", "invoice.xml",
RequestBody.create(new File("invoice.xml"), MediaType.parse("application/xml")))
.build();
try (Response response = new OkHttpClient().newCall(
new Request.Builder()
.url("https://api.invoicexml.com/v1/extract/json")
.header("Authorization", "Bearer " + apiKey)
.post(body).build()
).execute()) {
String json = response.body().string();
// Deserialize with Jackson, Gson, or your preferred JSON library.
// The invoice document sits under the "invoice" key: seller, buyer, lines, totals.
}Full example: ExtractJson.java | API reference | Sample response
Experimental feature. Human verification required before any production use.
Real-world PDF invoices are often messy: scanned at low quality, irregularly formatted, multi-page, or missing fields that EN 16931 requires. AI extraction can make subtle mistakes that automated validators may not catch: wrong tax category codes, transposed amounts, missing seller VAT identifiers, incorrect currency formatting.
Always review the output before submitting it to a public authority. See the AI conversion notes in the main README.
The endpoint takes the PDF plus a buyerReference form field: the Leitweg-ID cannot be inferred from the source document, so you must supply it.
Full example: AiConvert.java | API reference
XRechnung has no visual layer: the XML is the invoice, which is fine for machines and useless for the person in accounts payable who wants to read it. This endpoint renders the XML into a formatted PDF preview, auto-detecting whether the file is CII or UBL syntax. The PDF is for reading only; the XML file remains the authoritative invoice for compliance and tax purposes.
RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", "invoice.xml",
RequestBody.create(new File("invoice.xml"), MediaType.parse("application/xml")))
.addFormDataPart("language", "de") // en, de, or fr
.build();
Request request = new Request.Builder()
.url("https://api.invoicexml.com/v1/render/xrechnung/to/pdf")
.header("Authorization", "Bearer " + apiKey)
.post(body)
.build();
try (Response response = new OkHttpClient().newCall(request).execute()) {
Files.write(Paths.get("invoice-preview.pdf"), response.body().bytes());
}Full example: Render.java | API reference
Return an XRechnung invoice from a REST controller:
@RestController
public class XRechnungController {
@GetMapping(value = "/invoices/{id}/xrechnung", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> download(@PathVariable String id) throws Exception {
byte[] pdf = xrechnungService.create(id);
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=\"invoice-" + id + ".pdf\"")
.body(pdf);
}
}Inject the API key from application.properties via @Value("${invoicexml.api-key}").
@Path("/invoices")
public class XRechnungResource {
@GET
@Path("/{id}/xrechnung")
@Produces("application/pdf")
public Response download(@PathParam("id") String id) throws Exception {
byte[] pdf = xrechnungService.create(id);
return Response.ok(pdf)
.header("Content-Disposition", "attachment; filename=\"invoice-" + id + ".pdf\"")
.build();
}
}OkHttp is also Square's library for Android, so the examples run on Android 5.0+ unchanged. Move the call off the main thread using enqueue(), CoroutineScope, or RxJava. For Android 9+ ensure your network security config allows TLS to api.invoicexml.com (it does by default).
Bundle OkHttp into your Lambda deployment package or layer. Cold-start latency is minimal because OkHttp is small (~700 KB).
HTTP 401 Unauthorized: API key missing or invalid. Generate one at invoicexml.com/account/authentication and confirm you are sendingAuthorization: Bearer YOUR_API_KEY. A frequent cause: pasting the wholeBearer xxxvalue asapiKey, which sendsBearer Bearer xxx. SetapiKeyto the raw key only.HTTP 400 Bad Requeston Create: a required field is missing or malformed. Frequent causes:IssueDatenot in ISO format (YYYY-MM-DD),Currencynot in ISO 4217 (EUR,USD), country codes not in ISO 3166-1 alpha-2 (DE,FR).RequestBody.create()argument order: OkHttp 4.x reversed the parameter order from 3.x. UseRequestBody.create(File, MediaType)for 4.x. The 3.x signature(MediaType, File)is deprecated but still callable.Files.writeStringnot found: that method requires Java 11+. The examples useFiles.write(Path, byte[])which works on Java 8+.- TLS handshake failures on old JDKs: enable TLS 1.2 explicitly on Java 8 with
-Dhttps.protocols=TLSv1.2,TLSv1.3, or upgrade to Java 11+. - BR-DE- failures on Validate*: an XRechnung-specific field is missing. The most common are BR-DE-15 (no Leitweg-ID in
buyerReference), BR-DE-2 (no seller contact group), and BR-DE-1 (no seller electronic address).