Browser-side JavaScript examples for creating, validating, and parsing XRechnung electronic invoices using the InvoiceXML API. Self-contained HTML files using vanilla JavaScript with native fetch and FormData. Compatible with modern browsers: Chrome, Firefox, Safari, Edge. No build step, no framework, no dependencies.
For background on the XRechnung standard itself (what it is, the Leitweg-ID, legal status), see the main repository README.
API keys embedded directly in browser-side JavaScript are visible to every visitor through View Source or DevTools. Anyone can extract the key and use it against your InvoiceXML quota.
These examples are appropriate for:
- Local prototyping and testing
- Internal tools behind your company's authentication
- Single-user desktop apps wrapped in Electron or Tauri
For public-facing production websites, route requests through your own backend so the API key stays server-side. See the Node.js examples or PHP examples for server-side integration patterns.
Sign up and generate a key here:
→ https://www.invoicexml.com/account/authentication
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.
- Any modern browser:
- Chrome 95+
- Firefox 90+
- Safari 14+
- Edge 95+
- An InvoiceXML API key
- Optional: a local web server (
python -m http.server,npx serve, etc.) for testing. The examples also work by opening the.htmlfiles directly viafile://.
| File | Operation | API endpoint |
|---|---|---|
create.html |
Build an XRechnung 3.0 XML invoice | POST /v1/create/xrechnung |
validate.html |
Validate an XRechnung file against the KoSIT rules | POST /v1/validate/xrechnung |
extract-json.html |
Parse an XRechnung XML into JSON | POST /v1/extract/json |
ai-convert.html |
(Experimental) Convert a plain PDF to XRechnung with AI | POST /v1/transform/to/xrechnung |
render.html |
Render XRechnung XML into a human-readable PDF | POST /v1/render/xrechnung/to/pdf |
Each file is a complete, runnable HTML page. Open it in a browser, paste your API key into the <script> block, and click the button.
<button id="run">Generate invoice</button>
<script>
const payload = {
invoice: {
invoiceNumber: 'XR-2026-001',
issueDate: '2026-05-18',
currency: 'EUR',
buyerReference: '991-12345-67', // the Leitweg-ID (BT-10)
seller: {
name: 'Acme GmbH', vatIdentifier: 'DE123456789',
contact: { name: 'Max Mustermann', phone: '+49 30 12345678', email: 'billing@acme.de' },
electronicAddress: { identifier: 'DE123456789', schemeId: '9930' }, /* ... */
},
buyer: {
name: 'Bundesamt für Musterverwaltung',
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' },
};
document.getElementById('run').addEventListener('click', async () => {
const response = await fetch('https://api.invoicexml.com/v1/create/xrechnung', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + apiKey,
},
body: JSON.stringify(payload),
});
const xml = await response.text();
const blob = new Blob([xml], { type: 'application/xml' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-xrechnung.xml';
a.click();
});
</script>The XML is downloaded directly to the user's machine via URL.createObjectURL and a triggered <a download>. 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.
Full example: create.html | API reference
<input type="file" id="file" accept=".xml,application/xml,text/xml">
<button id="run">Validate</button>
<pre id="output"></pre>
<script>
document.getElementById('run').addEventListener('click', async () => {
const file = document.getElementById('file').files[0];
const form = new FormData();
form.append('file', file);
const response = await fetch('https://api.invoicexml.com/v1/validate/xrechnung', {
method: 'POST',
headers: { Authorization: 'Bearer ' + apiKey },
body: form,
});
document.getElementById('output').textContent = await response.text();
});
</script>The file is read directly from the <input type="file"> element. No FileReader or Blob conversion needed: FormData.append(file) accepts the File object directly.
Full example: validate.html | API reference
Useful for displaying invoice details in a web UI, populating form fields from an uploaded XRechnung file, or building drag-and-drop invoice review tools.
const form = new FormData();
form.append('file', fileInput.files[0]);
const response = await fetch('https://api.invoicexml.com/v1/extract/json', {
method: 'POST',
headers: { Authorization: 'Bearer ' + apiKey },
body: form,
});
const { invoice } = await response.json();
// The invoice document sits under the "invoice" key of the response.
console.log(invoice.seller.name, invoice.totals.grandTotalAmount);Full example: extract-json.html | API reference | Sample response
Experimental feature. Human verification required before any production use.
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 PDF before sending it to a customer or tax authority. See the AI conversion notes in the main README.
Full example: ai-convert.html | 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.
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('language', 'de'); // en, de, or fr
const response = await fetch('https://api.invoicexml.com/v1/render/xrechnung/to/pdf', {
method: 'POST',
headers: { Authorization: 'Bearer ' + apiKey },
body: form,
});
const blob = await response.blob();
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-preview.pdf';
a.click();Full example: render.html | API reference
The patterns below assume the API key is held server-side and proxied through your backend. Replace /api/xrechnung/... with your own proxy endpoints.
function CreateXRechnungButton() {
const [status, setStatus] = useState('');
async function handleClick() {
setStatus('Generating...');
const response = await fetch('/api/xrechnung/create', { method: 'POST' });
const blob = await response.blob();
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-xrechnung.xml';
a.click();
setStatus('Done');
}
return <button onClick={handleClick}>Generate invoice</button>;
}<template>
<button @click="handleClick">Generate invoice</button>
<p>{{ status }}</p>
</template>
<script setup>
import { ref } from 'vue';
const status = ref('');
async function handleClick() {
status.value = 'Generating...';
const response = await fetch('/api/xrechnung/create', { method: 'POST' });
const blob = await response.blob();
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-xrechnung.xml';
a.click();
status.value = 'Done';
}
</script><script>
let status = '';
async function handleClick() {
status = 'Generating...';
const response = await fetch('/api/xrechnung/create', { method: 'POST' });
const blob = await response.blob();
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-xrechnung.xml';
a.click();
status = 'Done';
}
</script>
<button on:click={handleClick}>Generate invoice</button>
<p>{status}</p>Use HttpClient with responseType: 'blob':
this.http.post('/api/xrechnung/create', payload, { responseType: 'blob' })
.subscribe(blob => {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'invoice-xrechnung.xml';
a.click();
});- CORS errors in the browser console: if the browser blocks the request, route through your own backend (proxy) instead of calling the API directly from the browser.
- API key visible in DevTools: this is expected when the key is embedded in client-side code. For anything beyond local prototyping, move the call to a server-side proxy.
HTTP 401 Unauthorized: API key missing or invalid. Generate one at invoicexml.com/account/authentication. A frequent cause: settingapiKeyto 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).- File input always empty:
fileInput.files[0]isundefineduntil the user actually picks a file. Always check before reading. - Mixed content warnings: if your page is served over
https://, all API calls must also behttps://. The examples usehttps://api.invoicexml.comso this is fine, just do not change it tohttp://. file://protocol limitations: opening the HTML directly viafile://works for these examples but breaks some browser APIs in stricter modes. Use a local server (python -m http.server,npx serve) if you hit issues.