Documentation
Everything you need to validate and generate compliant e-invoices. Base URL: https://stampbench.com
New here? Start on the developer platform overview or try it without installing anything in the playground.
Just need to send an invoice? You do not need any of this — create one with the invoice builder instead. It uses the same engine documented on this page, with none of the setup.
Quickstart
1. Install the library. Validation, generation and repair run locally — no key, no account, no network:
npm install @stampbench/core
import { validateXml } from '@stampbench/core';
const result = validateXml(xml, { profile: 'xrechnung' });
result.valid; // boolean
result.meta.rulesRun; // how many rules produced that verdict
for (const v of result.violations) {
console.error(v.ruleId, v.severity, v.message, v.line);
}2. Or call the hosted API. Anonymous calls are allowed (10/hour per IP), so you can try it before signing up:
curl -s https://stampbench.com/api/v1/validate \ -H "Content-Type: application/xml" \ --data-binary @invoice.xml
These are plain HTTPS calls, not SDKs — the only published package is @stampbench/core for JavaScript and TypeScript. Any language that can post JSON can use the API.
Keys are issued from your account page; if the hosted API is not yet switched on for you, email [email protected]. 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 [email protected]
en16931@2017 → [email protected] (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', '[email protected]');
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: '[email protected]',
label: 'XRechnung 3.1',
profile: 'xrechnung',
specVersion: 'XRechnung 3.1',
status: 'candidate', // published, not yet binding
effectiveFrom: '2027-01-01',
rules: [...getRuleset('[email protected]').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": 56, "rulesetVersion": "2026-08.2" }
}Query/body parameter profile: xrechnung (default, 56 rules — the European core plus the German BR-DE rules) or en16931 (40 rules, the European core alone). meta.rulesRun in the response always states which of the two actually ran, so a verdict is never ambiguous about the ruleset behind it. See markets for which profile applies where.
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" }Payment webhook
Auto-invoicing, for paid plans: generate a secret webhook URL on your account page and point your payment provider — or anything that can POST JSON — at it. A payment that names one of your invoice numbers marks that invoice paid; anything else becomes a draft invoice in your saved list, ready to complete. The URL is the authentication: treat it like a password, and rotate it from the account page if it leaks.
POST https://stampbench.com/api/hooks/payment/sbwh_…your-token…
Native Stripe events (checkout.session.completed, payment_intent.succeeded, invoice.paid) and PayPal events (PAYMENT.CAPTURE.COMPLETED, PAYMENT.SALE.COMPLETED) are understood as-is — add the URL as a webhook endpoint in their dashboards. From anywhere else, send the generic shape; only amount and currency are required:
{
"amount": "149.50", // major units; number or string
"currency": "GBP", // ISO 4217
"invoiceNumber": "INV-2026-000042", // optional — matches & marks paid
"payerName": "Acme GmbH", // optional
"payerEmail": "[email protected]", // optional
"description": "August retainer", // optional — becomes the line item
"id": "pay_8f3k2", // optional — makes retries idempotent
"date": "2026-08-25" // optional
}
// Response
{ "ok": true, "action": "created" } // or "marked_paid", "already_paid",
// "duplicate", "ignored"Deliveries are idempotent — a retried event with the same id lands as duplicate rather than a second invoice. Unrecognised event types answer 200 with "action": "ignored" so providers do not retry them forever. Auto-created invoices arrive marked paid, with the payment amount as a single zero-VAT line and a note saying where they came from — review the details and VAT before sending one to a customer.
Rate limits & quotas
| Plan | Calls / month | Rate limit |
|---|---|---|
| Anonymous | 10 / hour / IP | — |
| Free | 100 | 10 / min |
| Developer | 5,000 | 60 / min |
| Agency | 25,000 | 120 / min |
| Platform | 100,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.