# e-Factura Validator (Romania) – also Peppol BIS 3.0 and EN 16931
> Check a Romanian e-Factura (UBL 2.1, CIUS-RO 1.0.9) before uploading it to ANAF SPV, and get the exact fix for every error: plain-English explanation, the official Romanian rule text, the UBL path of the field and, for amount errors, the correct value computed from the invoice (chained, so following the report gives a consistent invoice). Also validates Peppol BIS Billing 3.0 and EN 16931. No account: $0.01 per invoice via x402, plus a small free tier.
Base URL: https://einvoice.alexhub.uk
OpenAPI: https://einvoice.alexhub.uk/openapi.json · MCP: https://einvoice.alexhub.uk/mcp · Short version: https://einvoice.alexhub.uk/llms.txt
## Prices and free quotas
| What | HTTP | MCP tool | Price |
|---|---|---|---|
| Validate an invoice (e-Factura / Peppol / EN 16931) | POST /v1/validate | validate_invoice | $0.01 |
| Validate an invoice, free | POST /v1/validate/free | validate_invoice_free | free, 3/day per IP |
| Auto-fix an invoice (corrected XML) | POST /v1/fix | fix_invoice | $0.05, only if something was fixed |
| Generate an e-Factura from JSON | POST /v1/generate | generate_invoice | $0.05 |
| Check a SAF-T D406 file | POST /v1/saft/validate | validate_saft | $0.50 |
| Check a SAF-T D406 file, free | POST /v1/saft/validate/free | validate_saft_free | free, 1/day per IP, first 10 findings |
| Romanian company by CUI (ANAF) | GET /v1/company/{cui} | lookup_company | free, 100/day per IP |
| BNR exchange rate | GET /v1/fx/{currency}?date= | fx_rate | free |
| Holidays, working days, tax deadlines | GET /v1/calendar/… | ro_holidays, ro_business_day, ro_tax_deadlines | free |
| IBAN check + Romanian bank | GET /v1/iban/{iban} | validate_iban | free |
| Explain a rule (BR-RO-…, BR-CO-…, PEPPOL-…) | GET /en/rules/{rule} (HTML) | explain_rule | free |
Paid calls: x402 (exact scheme), USDC on eip155:8453. No account, no API key, no gas for the payer.
Nothing is charged for input that is not a UBL Invoice/CreditNote (or not a D406 file): HTTP 422, no payment.
Free quotas reset at 00:00 UTC. All free calls together are also capped per day; when that cap is hit the free
endpoints answer 429 and point to the paid one.
## When to call
- An agent or program generated UBL invoice XML: validate before sending it to ANAF SPV / a Peppol access point.
- ANAF rejected an invoice: validate it, every finding says what to change; or let /v1/fix change it.
- You have invoice data but no XML: /v1/generate builds a validated e-Factura (parties filled from ANAF by CUI).
- Before filing SAF-T (D406): ANAF's own validator plus the accounting consistency checks ANAF runs later.
## Validate an invoice
Free (no wallet):
```
curl -X POST https://einvoice.alexhub.uk/v1/validate/free -H "Content-Type: application/xml" --data-binary @invoice.xml
```
Or JSON: `{"xml": "…", "profile": "auto"}` with Content-Type application/json.
Profiles: auto (reads CustomizationID), ro (Romania e-Factura CIUS-RO 1.0.9), peppol (Peppol BIS Billing 3.0),
en16931. Invoice and CreditNote, up to 2 MB.
Response (same on the paid endpoint):
```json
{
"valid": false,
"profile": "ro",
"document_type": "Invoice",
"errors": 1,
"warnings": 0,
"findings": [
{
"rule": "BR-CO-15",
"severity": "error",
"title": "Total with VAT is wrong",
"fix": "Set it to: total without VAT + total VAT.",
"ubl": "cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount",
"fields": [
"BT-112"
],
"expected": {
"field": "BT-112",
"value": "1210.00",
"found": "1190.00",
"currency": "RON"
}
}
]
}
```
- valid, errors, warnings: the verdict. Warnings do not make ANAF reject the invoice.
- findings[]: rule, severity (error | warning), title, fix (what to change, in words), ubl (path of the field),
fields (EN 16931 business terms), expected (the correct value for amount errors, computed from the invoice;
chained, so applying all of them gives a consistent invoice), message (the official rule text).
- autofix: present when /v1/fix can repair the errors for you.
- Rule pages: https://einvoice.alexhub.uk/en/rules/ (EN), https://einvoice.alexhub.uk/ro/erori/ (RO).
## Pay with x402 (paid endpoints)
1. Call the paid endpoint as usual. Without payment you get HTTP 402: the PAYMENT-REQUIRED header (base64 JSON)
holds the requirements (scheme exact, USDC on eip155:8453, amount, payTo); the JSON body repeats them and adds
what to do without a wallet.
2. Sign an EIP-3009 transferWithAuthorization for that amount with your wallet (no gas, no transaction by you).
3. Repeat the same request with the PAYMENT-SIGNATURE header. On success the PAYMENT-RESPONSE header carries the
settlement (transaction hash on Base).
Client libraries do all three steps: npm `@x402/fetch` (wrap fetch), pip `x402` (httpx/requests clients).
Payment is settled only when the response is billable (a real invoice / D406 was processed); 4xx/5xx are not charged.
Over MCP, the paid tools take the x402 payment in the request `_meta` (x402 MCP transport); a call without it
returns the payment requirements as a tool error, the same as the HTTP 402.
## Auto-fix
POST https://einvoice.alexhub.uk/v1/fix, same body as validate. Repairs county/sector/country/currency/unit codes, dates, VAT-id
prefixes, decimals, totals and VAT recomputed from the lines. Returns the corrected XML, every change (field, old,
new) and the new validation report. Not charged if nothing was fixed.
## Generate an e-Factura
POST https://einvoice.alexhub.uk/v1/generate:
```json
{"number": "FCT-1", "issue_date": "2026-10-30", "seller": {"cui": "RO…"}, "buyer": {"cui": "…"},
"lines": [{"name": "Consulting", "quantity": 2, "unit": "buc", "price": 100, "vat_rate": 21}],
"payment": {"iban": "RO…"}}
```
Names, addresses, county codes, Bucharest sectors and VAT status come from ANAF; any field you pass overrides them.
Totals and VAT are computed and the XML is validated before it is returned. Other currencies: "currency": "EUR"
(optionally "exchange_rate"; default = the BNR rate for the issue date); the VAT is also stated in RON (BR-RO-030).
## SAF-T (D406)
```
curl -X POST https://einvoice.alexhub.uk/v1/saft/validate/free -H "Content-Type: application/xml" --data-binary @D406.xml
```
Up to 100 MB over HTTP, 25 MB as text through MCP.
Monthly/quarterly (L, T, NL, NT), annual assets (A) and on-demand stocks (C); year and month are read from the Header.
Runs ANAF's official validator (DUKIntegrator) for the reporting period and the consistency checks it does not do
offline: balanced entries, GL totals, opening + movements = closing per account, trial balance, partner balances,
invoice totals, VAT = base x rate, VAT codes valid for the period, CUI/IBAN check digits, chart of accounts,
Amount = CurrencyAmount x ExchangeRate, periods.
Response: valid, errors, warnings, anaf{ok, errors}, consistency{errors}, period, summary,
findings[]: layer (anaf | consistency), rule, severity, location, title/title_ro, fix/fix_ro, expected{value, found}.
The free call returns the first 10 findings and the totals. Web page: https://einvoice.alexhub.uk/saft
## Free helpers
```
curl https://einvoice.alexhub.uk/v1/company/RO14399840 # ANAF data in e-Factura form: county RO-XX, SECTORn, VAT id, inactive
curl "https://einvoice.alexhub.uk/v1/fx/EUR?date=2026-10-30" # official BNR rate, RON per unit; /v1/fx = all currencies
curl "https://einvoice.alexhub.uk/v1/calendar/holidays?year=2026"
curl "https://einvoice.alexhub.uk/v1/calendar/business-day?date=2026-12-24&add=3"
curl "https://einvoice.alexhub.uk/v1/calendar/deadlines?period=2026-10" # monthly filing deadlines (D300, D406, D394, …)
curl https://einvoice.alexhub.uk/v1/iban/RO49AAAA1B31007593840000
```
## MCP
Streamable HTTP at https://einvoice.alexhub.uk/mcp, stateless, no authentication (the OAuth discovery paths answer 404 on purpose:
no metadata = no auth). 13 tools, all read-only and non-destructive: validate_invoice_free, validate_invoice,
fix_invoice, generate_invoice, validate_saft_free, validate_saft, lookup_company, explain_rule, fx_rate,
ro_holidays, ro_business_day, ro_tax_deadlines, validate_iban.
Claude Code:
```
claude mcp add --transport http einvoice https://einvoice.alexhub.uk/mcp
```
Claude Desktop (Settings > Connectors > Add custom connector, URL https://einvoice.alexhub.uk/mcp), or in claude_desktop_config.json:
```json
{"mcpServers": {"einvoice": {"command": "npx", "args": ["-y", "mcp-remote", "https://einvoice.alexhub.uk/mcp"]}}}
```
Cursor (~/.cursor/mcp.json):
```json
{"mcpServers": {"einvoice": {"url": "https://einvoice.alexhub.uk/mcp"}}}
```
VS Code (.vscode/mcp.json):
```json
{"servers": {"einvoice": {"type": "http", "url": "https://einvoice.alexhub.uk/mcp"}}}
```
Also in the official MCP registry as uk.alexhub/einvoice-validator (server card: https://einvoice.alexhub.uk/.well-known/mcp.json).
## Errors
| HTTP | Meaning | Charged |
|---|---|---|
| 200 | Report returned (also when the invoice has errors: valid=false) | yes, on paid endpoints |
| 400 | Bad request: empty body, invalid JSON, unknown profile, bad CUI | no |
| 402 | Payment required (paid endpoint without PAYMENT-SIGNATURE, or the payment was refused: see `error`) | no |
| 404 | Unknown company / rule / page | no |
| 413 | Body too large (2 MB invoices, 100 MB SAF-T) | no |
| 422 | Not a UBL Invoice/CreditNote (or not a D406 file); /v1/fix: nothing to fix; /v1/generate: input gives an invalid invoice | no |
| 429 | Free quota used up for today; the body names the paid endpoint | no |
| 502 | ANAF or BNR did not answer (company lookup, generate, fx) | no |
| 503 | Busy or starting; retry in a few seconds | no |
MCP tools return the same status and body inside an error result: `{"status": 429, "error": "…"}`.