Documentation

Everything you need to validate and generate compliant e-invoices. Base URL: https://stampbench.com

Quickstart

Validate a document straight away — anonymous calls are allowed (10/hour per IP):

curl -s https://stampbench.com/api/v1/validate \
  -H "Content-Type: application/xml" \
  --data-binary @invoice.xml

The hosted API is in early access — email hello@stampbench.com for a key. The library and CLI need no account at all.

Regression testing against a future rule set

E-invoicing rule sets change on a published cadence, and standards bodies release the artefacts before they become legally binding. That gap is your opportunity: you can find out which of your invoices will start failing while you still have time to fix them, instead of on the switchover date.

# Which rule sets can I compare?
npx stampbench rulesets

# Will my invoices survive the German rules?
npx stampbench regress ./invoices --from en16931@2017 --to xrechnung@3.0
en16931@2017 → xrechnung@3.0  (40 → 56 rules)

3 documents would START failing under XRechnung 3.0.

Fix these rules first (most documents affected):
  BR-DE-15     3 documents  Missing buyer reference (BT-10)…
  BR-DE-2      3 documents  XRechnung requires a seller contact (BG-6)…

Affected documents:
  invoices/eu-invoice-001.xml  BR-DE-2, BR-DE-5, BR-DE-6, BR-DE-7, BR-DE-15

5 checked · 3 regressions · 0 improvements · 2 unchanged pass · 0 unchanged fail

It exits 1 when anything regresses, so you can drop it into CI and fail the build the day a new rule set is published — long before it is enforced. Add --json for a machine-readable report (per-document transitions plus rules ranked by how many documents they break).

The same machinery works in the library, including against a rule set you register yourself — a newly published specification, or your own house rules:

import { compareRulesets, registerRuleset, getRuleset } from '@stampbench/core';

const report = compareRulesets(documents, 'en16931@2017', 'xrechnung@3.0');
report.summary.regressions;  // documents that pass today and would not tomorrow
report.byNewRule;            // ranked: fix the top one for the biggest win

// Register a new specification release the day it is published
registerRuleset({
  id: 'xrechnung@3.1',
  label: 'XRechnung 3.1',
  profile: 'xrechnung',
  specVersion: 'XRechnung 3.1',
  status: 'candidate',          // published, not yet binding
  effectiveFrom: '2027-01-01',
  rules: [...getRuleset('xrechnung@3.0').rules, ...myNewRules],
});

Superseded rule sets stay registered too, so you can answer an audit question like “would this invoice have been valid when we issued it?” by validating against the version that was in force at the time.

Authentication

Send your key in the Authorization header (or x-api-key):

Authorization: Bearer ig_live_4f3a…

Keys are shown once at creation and stored hashed. Revoke leaked keys instantly in the dashboard.

POST /api/v1/validate

Validates an e-invoice against EN 16931 plus (by default) the German XRechnung profile. Both syntaxes are auto-detected: UBL and CII (ZUGFeRD / Factur-X XML — what German companies mostly receive). Accepts raw XML (Content-Type: application/xml) or JSON:

// Request (JSON form)
{ "xml": "<?xml version=\"1.0\"?><Invoice …>", "profile": "xrechnung" }

// Response
{
  "valid": false,
  "profile": "xrechnung",
  "syntax": "ubl",            // or "cii" (ZUGFeRD/Factur-X)
  "errorCount": 1,
  "warningCount": 1,
  "violations": [
    {
      "ruleId": "BR-DE-15",
      "severity": "error",
      "message": "Missing buyer reference (BT-10). For German public-sector buyers this is the Leitweg-ID…",
      "terms": ["BT-10"]
    }
  ],
  "meta": { "rulesRun": 42, "rulesetVersion": "2026-08.1" }
}

Query/body parameter profile: xrechnung (default) or en16931 (core rules only, for non-German EU invoices).

POST /api/v1/generate

Generates XRechnung 3.0 UBL XML from a JSON invoice. Unless disabled, totals (BT-106…BT-115) and the VAT breakdown (BG-23) are computed from your lines so the BR-CO arithmetic rules pass by construction.

// Request
{
  "invoice": {
    "number": "RE-2026-0043",
    "issueDate": "2026-08-02",
    "currencyCode": "EUR",
    "buyerReference": "PO-2026-118",
    "seller": { "name": "…", "vatId": "DE123456789", "address": { … }, "contact": { … } },
    "buyer":  { "name": "…", "address": { … } },
    "payment": { "meansTypeCode": "58", "creditTransfers": [{ "iban": "DE89…" }] },
    "lines": [{
      "quantity": 12, "unitCode": "HUR",
      "item": { "name": "Beratung" },
      "price": { "netPrice": 120 },
      "vat": { "categoryCode": "S", "rate": 19 }
    }]
  },
  "options": { "computeTotals": true, "validate": true, "profile": "xrechnung" }
}

// Response
{ "xml": "<?xml version=\"1.0\" …", "validation": { "valid": true, … } }

POST /api/ai/explain

Turns the violations array from a validate call into a plain-language fix plan, written by Claude. Pass the violations exactly as you received them:

{ "violations": [ …from /api/v1/validate… ] }

// Response
{ "explanation": "Found 2 blocking errors…\n1. **BR-DE-15** …", "source": "claude" }

Rate limits & quotas

PlanCalls / monthRate limit
Anonymous10 / hour / IP
Free10010 / min
Developer5,00060 / min
Agency25,000120 / min
Platform100,000+300 / min

Responses include X-Quota-Used, X-Quota-Limit and X-RateLimit-Remaining headers.

Error format

{ "error": { "code": "quota_exceeded", "message": "Monthly quota reached (100/100 …)" } }

// Codes: invalid_request, invalid_json, payload_too_large,
//        rate_limited (429), quota_exceeded (402), unauthenticated (401)

The open-source library

@stampbench/core (MIT) runs the same validation and generation locally — unlimited, offline, free forever:

npm install @stampbench/core

import {
  validateUblXml,       // XML → violations
  validateInvoice,      // semantic model → violations
  generateXRechnungUbl, // semantic model → XRechnung UBL
  withComputedTotals,   // fills BG-22 totals + BG-23 VAT breakdown
} from '@stampbench/core';

const result = validateUblXml(xml, { profile: 'xrechnung' });
if (!result.valid) console.table(result.violations);

The hosted API adds always-current rules, AI explanations, cross-language access, usage history, and zero maintenance.

Rules coverage

Stampbench implements a documented, growing subset of the official rule sets — currently:

  • EN 16931 structural rules: BR-01…BR-16, per-line BR-21…BR-27, BR-CO-04
  • Totals & VAT arithmetic: BR-CO-10, -13, -14, -15, -16, -17, -18, -25
  • VAT category families — BR-S, BR-Z, BR-E, BR-AE, BR-K, BR-G, BR-O: breakdown presence, rate constraints, per-category taxable/VAT arithmetic, exemption reasons
  • XRechnung (German CIUS): BR-DE-1…9, BR-DE-15, BR-DE-16, BR-DE-17, BR-DE-21, plus credit-transfer/IBAN checks
  • Format & code-list diagnostics (IG-* rule ids): ISO dates, currency/country codes, UNCL 5305 VAT categories

Syntax coverage: UBL and CII (ZUGFeRD/Factur-X XML) for validation, UBL for generation. Every result is version-pinned to the spec release it was checked against (EN 16931-1:2017, XRechnung 3.0 — see meta.specVersions). Stampbench is developer tooling, not legal advice — for certification-grade sign-off, also run the official KoSIT validator in CI. Our goal is that by the time you run it, it passes — and we publish our divergence from the KoSIT validator rather than asking you to take that on faith.