Python client SDK for the Xingen e-invoice validation API
Project description
xingen
Python client SDK for the Xingen e-invoice validation API — submit UBL, CII, ZUGFeRD, and SAP IDoc/OData invoices for validation against EN16931, XRechnung, and Peppol.
Requires Python 3.10+. Built on requests and pydantic v2.
Status: v1, covering invoice submission/validation and API key management. Contacts and dashboard/user endpoints are not exposed (they're Firebase-auth-only on the backend).
Install
pip install xingen-sdk
(The distribution is named xingen-sdk on PyPI since xingen was already taken by an unrelated
project, but the import name is still xingen: from xingen import XingenClient.)
Authentication
Every request needs an API key (xgn_live_... for production, xgn_test_... for sandbox — sandbox
requests never count toward quota). Create one from the Xingen dashboard or via client.api_keys.
from xingen import XingenClient
client = XingenClient(api_key=os.environ["XINGEN_API_KEY"])
XingenClient holds one connection-pooled requests.Session — construct it once and reuse it,
don't rebuild it per request. base_url=... overrides the default https://app.xingen.de/api,
useful for self-hosted or local (./gradlew bootRun, port 10001) testing.
Validate a file
Every validate/submit endpoint is asynchronous — the backend queues the invoice and returns
immediately. Use a *_and_wait helper to submit and poll for the result in one call:
from xingen.models.enums import InvoiceStatus, ValidationProfile
result = client.invoices.validate_file_and_wait("invoice.xml", ValidationProfile.XRECHNUNG)
if result.status is InvoiceStatus.VALIDATED and result.validation_result.valid:
print("Valid!")
else:
for error in result.validation_result.errors:
print(f"{error.severity}: {error.message} ({error.field})")
PollOptions controls the backoff (initial_interval, max_interval, backoff_multiplier), the
overall timeout, and an optional cancellation_check. A failed validation is not an exception
— it's a completed API call that found the invoice invalid, so *_and_wait returns normally with
validation_result.valid == False. Only a transport failure, cancellation, or timeout raises.
from xingen.invoices import PollOptions
options = PollOptions(initial_interval=0.3, max_interval=3.0, timeout=30.0)
result = client.invoices.validate_file_and_wait("invoice.xml", ValidationProfile.XRECHNUNG, options)
If you'd rather manage polling yourself, use the low-level pair:
submitted = client.invoices.validate_file("invoice.xml", ValidationProfile.EN16931)
# ... later ...
record = client.invoices.get(submitted.id)
validate_idoc / validate_idoc_and_wait work the same way for SAP IDoc XML files. Both, and
validate_file/validate_file_and_wait, also accept (filename, content_bytes) tuples if you
already hold the file bytes in memory instead of a path.
Submit a structured invoice (JSON)
from datetime import date
from decimal import Decimal
from xingen.invoices import InvoiceSubmission, LineInput, PartyInput
submission = InvoiceSubmission(
invoice_number="INV-2024-0042",
issue_date=date(2024, 3, 15),
currency="EUR",
buyer_reference="991-12345-06",
validation_profile=ValidationProfile.XRECHNUNG,
supplier=PartyInput(name="Acme GmbH", vat_id="DE123456789"),
buyer=PartyInput(name="Buyer Co", leitweg_id="991-12345-06"),
lines=[
LineInput(
description="Software License Q1",
quantity=Decimal("5"),
unit="C62",
price=Decimal("199.00"),
tax_rate=Decimal("19"),
)
],
)
result = client.invoices.submit_and_wait(submission)
SAP S/4HANA OData supplier-invoice payloads are supported as a thin passthrough — pass raw JSON or
a dict rather than a fully typed model:
client.invoices.submit_odata(raw_odata_json, ValidationProfile.EN16931)
List and retrieve invoices
page = client.invoices.list(0, 20, "createdAt,desc")
# or, to walk every invoice without managing page indices yourself:
for record in client.invoices.list_all(50):
print(record.id, "->", record.status)
one = client.invoices.get("inv_01HXYZ")
Download results
pdf = client.invoices.download_pdf(id) # ZUGFeRD PDF with embedded XML
idoc_xml = client.invoices.download_idoc_xml(id) # SAP IDoc XML
API keys
from xingen.apikeys import CreateApiKeyRequest
created = client.api_keys.create(CreateApiKeyRequest(name="Production CI", sandbox=False))
print("Store this now, it's shown only once:", created.raw_key)
keys = client.api_keys.list()
client.api_keys.revoke(created.id)
Error handling
All SDK exceptions extend XingenException. HTTP errors map to typed subclasses of ApiException:
| Exception | Status | Notes |
|---|---|---|
AuthenticationException |
401 | Missing or invalid API key |
PermissionException |
403 | Resource exists but isn't owned by the caller |
NotFoundException |
404 | |
ValidationRequestException |
400 | .field_errors has details for request-body validation failures |
QuotaExceededException |
429 | Monthly request quota exhausted |
ApiException |
other 4xx/5xx | Fallback; .status_code / .raw_body always available |
from xingen.error import QuotaExceededException, ValidationRequestException, XingenException
try:
client.invoices.submit(submission)
except ValidationRequestException as e:
for field, message in e.field_errors.items():
print(f"{field}: {message}")
except QuotaExceededException:
print("Quota exceeded — upgrade or wait for the next billing period")
except XingenException as e:
print(f"Request failed: {e}")
Design notes
- No automatic retries. Retrying a
submit()after a client-side timeout is unsafe without idempotency keys, which the API doesn't support yet — a retried submit could create a duplicate invoice. Handle retries at the call site if you need them. - Monetary and quantity fields are
Decimal, neverfloat. JSON is parsed through Python's stdlibjsonmodule withparse_float=Decimalrather than pydantic's native (float64-lossy) JSON parser, so exact literal precision is preserved end to end.
Contributing
pip install -e ".[dev]"
ruff check .
mypy src/xingen
pytest
Tests run against a real (loopback) http.server.HTTPServer, not a mocking framework — no network
calls leave the machine, and no external test-server dependency is required.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xingen_sdk-0.1.0.tar.gz.
File metadata
- Download URL: xingen_sdk-0.1.0.tar.gz
- Upload date:
- Size: 22.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b39d85939092890d94c596f44f74d40817b38f97bc311de8e50ea4812895133d
|
|
| MD5 |
34129274264b0136aa6031a777b60839
|
|
| BLAKE2b-256 |
bed5fc57da2278f76823a277c443c6b4997f4c7c0d67b0bb6de3e5b49554ef9a
|
Provenance
The following attestation bundles were made for xingen_sdk-0.1.0.tar.gz:
Publisher:
publish.yml on nko-technologies/xingen-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xingen_sdk-0.1.0.tar.gz -
Subject digest:
b39d85939092890d94c596f44f74d40817b38f97bc311de8e50ea4812895133d - Sigstore transparency entry: 2130100415
- Sigstore integration time:
-
Permalink:
nko-technologies/xingen-python-sdk@536a7750c2cef6b0f786bc5cd345f111839b0a32 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nko-technologies
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@536a7750c2cef6b0f786bc5cd345f111839b0a32 -
Trigger Event:
push
-
Statement type:
File details
Details for the file xingen_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: xingen_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1888edcfa37b143ece4c9bae3fd8d4c802d44d853c742bd3ee4fa365a8110f00
|
|
| MD5 |
85ff518603494ae79994b55f876c62f8
|
|
| BLAKE2b-256 |
627942da227300ed71570e2d3c11508e47fa4ea4812ba484ebc1f3e227ebd55f
|
Provenance
The following attestation bundles were made for xingen_sdk-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on nko-technologies/xingen-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xingen_sdk-0.1.0-py3-none-any.whl -
Subject digest:
1888edcfa37b143ece4c9bae3fd8d4c802d44d853c742bd3ee4fa365a8110f00 - Sigstore transparency entry: 2130100494
- Sigstore integration time:
-
Permalink:
nko-technologies/xingen-python-sdk@536a7750c2cef6b0f786bc5cd345f111839b0a32 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nko-technologies
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@536a7750c2cef6b0f786bc5cd345f111839b0a32 -
Trigger Event:
push
-
Statement type: