Developers · Quickstart
Your first call, in three steps.
A test key without an account, one POST with a sample invoice, and the protocol that comes back. The key allows 10 requests and expires after one hour.
- 01
Get a test key
nowNo account. 10 requests, valid for one hour. From the shell it is one line:
curl -X POST https://www.invoicenavigator.eu/api/developers/instant-key
- 02
Send a sample invoice
nextOne call: validate, patch what is structural, re-validate, and sign an evidence pack. The sample is an XRechnung 3.0 UBL invoice missing several mandatory fields.
curl curl -X POST https://www.invoicenavigator.eu/api/v2/validate-and-fix \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d "$(jq -n --rawfile xml invoice.xml '{xml: $xml, autoFix: true}')" - 03
Read the protocol
nextThe response carries
originalValid,fixedValid,fixesApplied,remainingIssuesand, when the file changed,fixedXml.
The same call in JavaScript and Python
Put a permanent key in INVOICE_NAV_API_KEY. Keys are created on sign-up and listed under Dashboard → API; a sk_test_ key is issued next to every sk_live_ key.
import { readFileSync } from 'node:fs'
const res = await fetch('https://www.invoicenavigator.eu/api/v2/validate-and-fix', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.INVOICE_NAV_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
xml: readFileSync('invoice.xml', 'utf8'),
fileName: 'invoice.xml',
autoFix: true,
}),
})
const { success, data, error } = await res.json()
if (!success) throw new Error(`${error.code}: ${error.message}`)
if (data.originalValid) console.log('already valid', data.validationRef)
else if (data.fixedValid) console.log('fixed', data.fixesApplied, 'changes')
else for (const i of data.remainingIssues) console.log(i.code, i.message)import os, requests
with open("invoice.xml", encoding="utf-8") as f:
xml = f.read()
r = requests.post(
"https://www.invoicenavigator.eu/api/v2/validate-and-fix",
headers={"Authorization": f"Bearer {os.environ['INVOICE_NAV_API_KEY']}"},
json={"xml": xml, "fileName": "invoice.xml", "autoFix": True},
)
body = r.json()
if not body["success"]:
raise SystemExit(f"{body['error']['code']}: {body['error']['message']}")
data = body["data"]
if data["originalValid"]:
print("already valid", data["validationRef"])
elif data.get("fixedValid"):
print("fixed", data["fixesApplied"], "changes")
else:
for issue in data["remainingIssues"]:
print(issue["code"], issue["message"])Or install the typed client: npm install @invoicenavigator/sdk.
Four fields decide what you do next
- originalValid
- The file passed as sent. Nothing was changed; deliver it.
- fixedValid
- The file passes after structural patches. data.fixedXml is the file to deliver; fixesApplied says how many edits were made.
- fixSummary.needsInput
- Business data is missing (a buyer reference, a Leitweg-ID). POST the values to data._links.fixWithInput. Not offered on test keys.
- remainingIssues
- Codes that still fail after patching. Each carries code, severity and message; look the code up under /errors.
Every response is wrapped: { success, data, meta } on success, { success: false, error: { code, message } } on failure. Full contract in the API reference; validation only, without patching, is POST /v1/validate (reference).
- API referenceEvery endpoint, generated from the OpenAPI spec.
- AuthenticationKey prefixes, the Bearer header, error codes.
- Test keysWhat sk_test_ keys can and cannot do.
- ProductionHeaders, retries, limits, the go-live list.
- Error codesEvery validation code explained, with the fix.