The API is the product.
Five calls from an empty organization to the first compliant invoice. One data model for finance, engineering and ops: documented, versioned, replay-safe.
Five calls to the first invoice.
No setup wizard, no console clicking. Everything Kontier can do is an API call. This is the shortest path.
- 01
customer.createdCreate the customer
customer_type drives the tax treatment. For BUSINESS in the EU the VAT ID is mandatory, and the VIES check runs automatically.
terminalcurl -X POST https://api.kontier.eu/v1/customers \ -H "Authorization: Bearer sk_test_..." \ -H "Idempotency-Key: 7f3c9a1e" \ -d '{"name":"Acme GmbH","customer_type":"BUSINESS", "billing_address":{"country":"DE"}, "tax_identifiers":{"vat":"DE123456789"}}'→ 201 · customer.created · VIES: valid
- 02
product.createdDefine the product
The billable unit. pricing_model decides how tiers compute later: VOLUME, STAIRCASE or PACKAGE.
terminalcurl -X POST https://api.kontier.eu/v1/products \ -H "Idempotency-Key: 2b81d4f0" \ -d '{"name":"API Platform","pricing_model":"STAIRCASE", "tax_category":"STANDARD"}'→ 201 · product.created
- 03
plan.createdVersion the plan
The plan bundles products and prices. Every publish mints an integer plan_version, and subscriptions pin it.
terminalcurl -X POST https://api.kontier.eu/v1/plans \ -H "Idempotency-Key: 9c02aa17" \ -d '{"name":"Pro"}'# then: POST /v1/plans/{id}/products→ 201 · plan.created · plan_version: 1
- 04
subscription.activatedStart the subscription
Without items the subscription inherits the plan-version snapshot. Trial, proration and SEPA mandate included.
terminalcurl -X POST https://api.kontier.eu/v1/subscriptions \ -H "Idempotency-Key: e51f7b93" \ -d '{"customer_id":"cus_8f2k","plan_id":"plan_pro", "billing_interval_unit":"MONTH","currency":"EUR"}'→ 201 · subscription.activated · pinned
- 05
invoice.createdSend usage, get an invoice
quantity is a string, so decimals survive the wire. The in-body idempotency_key dedupes the event itself.
terminalcurl -X POST https://api.kontier.eu/v1/usage-events \ -H "Idempotency-Key: evt_9a2f" \ -d '{"subscription_id":"sub_44c1","metric_key":"api_calls", "quantity":"18402","idempotency_key":"evt_9a2f"}'→ 202 · queued → invoice.created · €412.80
What the API guarantees.
Commitments, not a feature list: what every response, every error and every webhook is bound to honor.
Signed, at-least-once, replayable.
HMAC-SHA256 over the raw body, secret prefixed whsec_. Six delivery attempts (1m, 5m, 30m, 2h, 12h), dedup on the envelope id. No signing secret means no delivery - fail closed.
RFC 9457 Problem Details.
Every error is machine-readable: type, title, status, detail, code - plus a request_id for support. No guessing, no regex on error strings.
Required, not optional.
Idempotency-Key is required on every POST. The key is bound to the SHA-256 of the body: the same key with a different body returns 409. 5xx responses are never cached, so a retry really re-executes.
Cursor, not offset.
Keyset over (created_at, id), cursor as an opaque token. Stable under parallel inserts - has_more is the authoritative signal, not total_count.
Fail closed where it counts.
If the idempotency store falters, money-moving routes (/charge, /refund, /capture, /finalize) return 503 rather than run without dedup. A retry beats a double charge.
The plan version is pinned.
Subscriptions hold an integer plan_version. Catalogue changes never alter running invoices; switching is an explicit call, never a side effect.
The spec is the contract.
OpenAPI 3.1, generated straight from the code - not maintained on the side. 324 paths, 453 operations, 859 schemas and 114 documented webhook events. Client generation included.
The invoice that passes validation.
XRechnung and ZUGFeRD come out of the same invoice you already produce. No second data path, no export to Excel, no library you have to keep aligned with the KoSIT rules yourself.
From call to valid XML
Generate the document: pick a profile, reference the invoice.
POST /v1/e-invoicing/documents{ "invoice_id": "inv_7c21", "profile": "XRECHNUNG_3_0_UBL", "jurisdiction": "DE" }
Fetch the raw XML: exactly what the recipient receives.
GET /v1/e-invoicing/documents/{id}/raw→ 200 · application/xml
Read the findings: every violated rule by id, not "invalid".
GET /v1/e-invoicing/documents/{id}/findings→ [ { "rule": "BR-DE-15", … } ]
What gets validated
Own rule engine
EN 16931, KoSIT BR-DE and PEPPOL BIS run as rules inside the service - no external schematron service that can be down.
Leitweg-ID, actually checked
BR-DE-1 requires the BuyerReference (BT-10). BR-DE-15 checks its shape - this exact pattern:
^[0-9A-Z]{2,12}(-[0-9A-Z]{0,30})?-[0-9]{2}$ Quarantine, not a silent failure
Findings are stored per document. A document that breaks the rules is quarantined instead of quietly going out to a public authority.
17 profiles in the catalogue
- XRECHNUNG_3_0_UBL
- XRECHNUNG_3_0_CII
- EN16931_UBL_2_1
- EN16931_CII_D16B
- PEPPOL_BIS_BILLING_3_UBL
- ZUGFERD_2_COMFORT
- FACTURX_EN16931
- ZUGFERD_2_XRECHNUNG
- +9
Hybrid PDF/A-3 profiles (Factur-X, ZUGFeRD) need the Mustang service; XML-only profiles run without it.
Artifacts are archived with bytes and a hash chain, so the GoBD question "is this still the same invoice" has an answer.
Everything else lives in the reference.
Complete endpoints, schemas, webhook catalogs and examples, readable without signing up, instantly usable with sandbox keys. And anywhere on this site, pressing i reveals each artifact's API path, including right here.
Evaluating the API first?
We write when the API changes: new endpoints, new SDKs, breaking changes announced before they land.
Almost there - please confirm your email.
We've sent you a confirmation email. You are on the list only once you click the link inside. Nothing arrived? Check your spam folder.