PHP code samples for creating, validating, and parsing XRechnung electronic invoices using the InvoiceXML API. Compatible with PHP 7.0+ (PHP 8.x recommended), works in plain scripts, Laravel, Symfony, WordPress, WooCommerce, Drupal, Magento, or any other PHP framework. Zero Composer dependencies: uses PHP's built-in cURL extension.
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.
- PHP 7.0 or later (PHP 8.2+ recommended)
- The cURL extension (
php-curl), bundled with most PHP installations by default - No Composer, no external libraries
These examples use the built-in curl_* functions and CURLFile (available since PHP 5.5), so they run on essentially any modern PHP install without composer install.
| File | Operation | API endpoint |
|---|---|---|
create.php |
Build an XRechnung 3.0 XML invoice | POST /v1/create/xrechnung |
validate.php |
Validate an XRechnung file against the KoSIT rules | POST /v1/validate/xrechnung |
extract-json.php |
Parse an XRechnung XML into JSON | POST /v1/extract/json |
ai-convert.php |
(Experimental) Convert a plain PDF to XRechnung with AI | POST /v1/transform/to/xrechnung |
render.php |
Render XRechnung XML into a human-readable PDF | POST /v1/render/xrechnung/to/pdf |
Each file is standalone and runnable with php create.php. Open the file, replace YOUR_API_KEY with your real key, and execute.
Note on the snippets below: they are excerpts from those files and assume
$apiKeyis already defined. When in doubt, copy the complete file.
$payload = [
'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'],
];
$ch = curl_init('https://api.invoicexml.com/v1/create/xrechnung');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey,
],
]);
$xml = curl_exec($ch);
file_put_contents('invoice-xrechnung.xml', $xml);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.php | API reference
$ch = curl_init('https://api.invoicexml.com/v1/validate/xrechnung');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('invoice.xml', 'application/xml'),
],
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$report = curl_exec($ch);
echo $report;Returns a JSON validation report listing any rule failures (EN 16931 BR-* and BR-CO-, plus the German BR-DE- rules).
Full example: validate.php | API reference
Useful for feeding XRechnung invoices into REST APIs, ERPs, accounting systems, or any pipeline that prefers JSON over XML.
$ch = curl_init('https://api.invoicexml.com/v1/extract/json');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['file' => new CURLFile('invoice.xml', 'application/xml')],
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$json = curl_exec($ch);
$data = json_decode($json, true);
// The invoice document sits under the "invoice" key of the response.
echo $data['invoice']['seller']['name'];Full example: extract-json.php | 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.
$ch = curl_init('https://api.invoicexml.com/v1/transform/to/xrechnung');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('plain-invoice.pdf', 'application/pdf'),
'buyerReference' => '991-12345-67',
],
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$xml = curl_exec($ch);
file_put_contents('converted-xrechnung.xml', $xml);Full example: ai-convert.php | 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.
$ch = curl_init('https://api.invoicexml.com/v1/render/xrechnung/to/pdf');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('invoice.xml', 'application/xml'),
'language' => 'de', // en, de, or fr
],
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$pdf = curl_exec($ch);
file_put_contents('invoice-preview.pdf', $pdf);Full example: render.php | API reference
Return an XRechnung invoice from a controller or route closure:
use Illuminate\Support\Facades\Route;
Route::get('/invoices/{id}/xrechnung', function ($id) {
$pdf = app(XRechnungService::class)->create($id);
return response($pdf, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => "attachment; filename=\"invoice-{$id}.pdf\"",
]);
});Store the API key in config/services.php and read it via config('services.invoicexml.key').
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class InvoiceController
{
#[Route('/invoices/{id}/xrechnung', name: 'xrechnung_download')]
public function download(int $id): Response
{
$pdf = $this->xrechnungService->create($id);
return new Response($pdf, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => "attachment; filename=\"invoice-{$id}.pdf\"",
]);
}
}Hook into WooCommerce order lifecycle events to generate XRechnung invoices automatically:
add_action('woocommerce_order_status_completed', function ($order_id) {
$order = wc_get_order($order_id);
$pdf = create_xrechnung_from_order($order);
file_put_contents(WP_CONTENT_DIR . "/invoices/{$order_id}.pdf", $pdf);
});The same pattern works in Magento, Drupal Commerce, and PrestaShop.
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: setting$apiKeyto the wholeBearer xxxvalue, which sendsBearer Bearer xxx. Set 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).Class "CURLFile" not found: PHP cURL extension is missing. Install withapt-get install php-curl(Debian/Ubuntu),yum install php-curl(RHEL), or enable the extension inphp.ini.SSL certificate problemerrors: the server's CA bundle is out of date. Update OpenSSL on the host, or setCURLOPT_CAINFOto a currentcacert.pemfrom curl.se/ca/.- 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).