INVITE-ONLY PRIVATE BETA

E-invoicing compliance,
as a small, boring API.

Send structured JSON and receive a Peppol BIS Billing 3.0 or XRechnung 3.0 UBL invoice. No invoice storage, no sales call, and no need to maintain the XML generation rules inside your product.

Request an API key

Free during beta · 60 requests/minute · No SLA

Full API reference: browse the rendered reference · openapi.json · error catalog

Get started in four steps

  1. Request a key. Email us with your product and rough invoice volume. We send a key shaped like fkt_live_<id>_<secret> over a private channel. It is shown once — store it as a secret.
  2. Set it as an environment variable so it never lands in source control:
    export FAKTURATE_API_KEY="fkt_live_…"
  3. Make your first call. POST a JSON invoice to /v1/invoices (see below) and save the returned UBL XML.
  4. Handle the response. A 200 returns XML; anything else is a problem+json object you can branch on by type.

Authentication

Send your key as a bearer token on every request. Keys are server-to-server only — browser CORS is intentionally disabled because a key cannot be kept secret in frontend code.

Authorization: Bearer fkt_live_<id>_<secret>

One endpoint

POST https://api.fakturate.com/v1/invoices
Authorization: Bearer fkt_live_<id>_<secret>
Content-Type: application/json
Idempotency-Key: 2026-042            # optional

200 OK
Content-Type: application/xml
X-Fakturate-Api-Version: 1.0.0-experimental
X-Fakturate-Validation: input-semantic-en16931-subset
X-Fakturate-Schematron: not-run

Example request

curl https://api.fakturate.com/v1/invoices \
  --header "Authorization: Bearer $FAKTURATE_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: 2026-042" \
  --output invoice.xml \
  --data '{
    "profile": "peppol_bis_3",
    "invoice_number": "2026-042",
    "issue_date": "2026-06-15",
    "due_date": "2026-07-15",
    "currency": "EUR",
    "language": "en",
    "supplier": {
      "name": "Example Supplier BV",
      "tax_id": "BE0123456749",
      "address": { "street": "Example Street 12", "postal_code": "2000", "city": "Antwerp", "country_code": "BE" },
      "email": "billing@example.test",
      "phone": "+32 470 000000",
      "iban": "BE71096123456769",
      "contact_name": "Example Contact"
    },
    "customer": {
      "name": "Example Customer BV",
      "tax_id": "BE0987654394",
      "address": { "street": "Customer Street 1", "postal_code": "1000", "city": "Brussels", "country_code": "BE" },
      "email": "accounts@example.test",
      "reference": "PO-4711"
    },
    "vat_treatment": "standard",
    "payment_terms": "Pay within 30 days.",
    "lines": [
      { "description": "Design services", "quantity": 1.5, "unit_code": "HUR", "unit_price": 100, "vat_rate": 21 }
    ]
  }'

There is no SDK to install: the API is a single HTTP call, so the standard HTTP client in any language works. The complete machine-readable contract powers the rendered reference and is served at api.fakturate.com/openapi.json.

Idempotency

Invoice generation is a pure, side-effect-free function of the request body — nothing is stored, so resending the same request always yields the same XML. Retrying after a network timeout is therefore safe. Pass an optional Idempotency-Key header (1–255 printable ASCII characters) to correlate retries in your own logs; we echo it back on success. An invalid key returns invalid-idempotency-key.

Versioning

The major version is pinned in the URL path (/v1/). A /v1/ route never changes its request or response contract in a breaking way; breaking changes would ship under a new path such as /v2/. Backward-compatible additions (new optional fields or enum values) may appear within /v1/, so write your client to ignore unknown fields. Every response advertises the current revision in the X-Fakturate-Api-Version header.

Errors

Errors use the RFC 9457 application/problem+json format. Branch on the type URI, which dereferences to human-readable docs. Validation failures additionally carry an errors[] array of { path, code, message } objects pointing at each offending field.

{
  "type": "https://api.fakturate.com/problems/invalid-invoice",
  "title": "Invalid Invoice",
  "status": 422,
  "detail": "The invoice request failed validation.",
  "errors": [
    { "path": "$.lines[0].vat_rate", "code": "out_of_range", "message": "Must be at most 100." }
  ]
}
HTTPtypeWhen
401unauthorizedMissing, unknown, or revoked API key.
415unsupported-media-typeContent-Type is not application/json.
400malformed-jsonBody is not valid JSON.
400invalid-idempotency-keyIdempotency-Key is malformed.
413payload-too-largeBody exceeds 256 KiB.
422invalid-invoiceInput or semantic validation failed; see errors[].
429rate-limit-exceededOver 60 requests/minute; honour Retry-After.
500internal-errorGeneration failed; no invoice data leaks or is retained.

The complete list, including every field-level code, lives at api.fakturate.com/problems.

Privacy boundary

API requests are encrypted in transit with HTTPS. Fakturate briefly processes the invoice JSON and generated XML in the API Worker's memory, then returns the result. Invoice contents are not retained, cached, written to logs, or sent to another service.

We retain only bounded operational measurements: a pseudonymous API-key ID, route, requested profile, outcome status and latency bucket. These measurements contain no names, addresses, VAT numbers, invoice numbers, amounts, request bodies, generated XML, authorization headers, raw IP addresses or user agents. See the privacy policy.

What V1 validates

Every request is checked in three layers before any XML is generated, reflected in the X-Fakturate-Validation header (input-semantic-en16931-subset):

  1. Input. Strict JSON shape, types, ranges, lengths, and code lists (currency, country, unit of measure, VAT treatment). Unknown properties are rejected.
  2. Semantic. Profile and VAT-treatment requirements — e.g. reverse charge needs the buyer's VAT number, XRechnung needs a buyer reference and seller contact.
  3. EN 16931 business-rule subset. The input-dependent rules that most often cause downstream rejection: per-country VAT identifier structure (VIES), IBAN check digits (ISO 13616), and intra-EU VAT treatments restricted to EU parties.

The generation engine itself is regression-tested offline against the official EN 16931, Peppol and KoSIT rule sets, and satisfies the structural and calculation rules by construction.

V1 does not run the official XSD or Schematron rule sets separately for each request (X-Fakturate-Schematron: not-run). The EN 16931 subset above is a fast, high-value approximation, not a replacement. Full per-request UBL XSD and EN 16931 / Peppol / XRechnung Schematron validation is planned for V2.

Current limits