Romania's e-Factura is mandatory, and the hard part isn't the idea — it's ANAF's stack. You hit SOAP endpoints, an OAuth + digital-certificate lifecycle, and rejection codes that read like hex dumps. This is the field guide I wish I'd had: what actually makes ANAF reject an invoice, and how to read the error.
Why does ANAF reject my e-Factura?
Almost every rejection falls into one of four buckets:
- Schema / RO_CIUS validation — the XML is well-formed but breaks a business rule of the Romanian CIUS (the national customization of the EU standard, EN 16931). This is the most common and the most cryptic.
- Missing or malformed mandatory fields — a required party identifier, tax category, or total is absent or in the wrong shape.
- Certificate / OAuth lifecycle — your token expired, the cert isn't registered in SPV, or the auth handshake failed.
- SPV submission state — the document was accepted for processing but later rejected, and you have to poll for the verdict.
If you can classify a failure into one of these four, you already know where to look.
What is RO_CIUS and why does it fail when my XML "looks fine"?
RO_CIUS is Romania's Core Invoice Usage Specification — a national profile on top of the EU standard EN 16931. Your XML can be valid UBL and still violate a RO_CIUS rule, because CIUS adds country-specific constraints the base schema doesn't enforce. Typical categories:
- Party identification — the supplier/customer tax ID (CUI/CIF) or registration number missing, malformed, or inconsistent with the VAT-payer flag.
- Tax breakdown — the VAT category codes and the tax subtotals don't reconcile with the document totals (a rounding or category mismatch).
- Currency & dates — wrong currency code, or a date format/issue-date rule the CIUS tightens.
- Mandatory business terms — an EN 16931 business term (the "BT-…" fields) that RO_CIUS marks required is absent.
The trap: the base UBL schema passes, so your XML editor says "valid," but ANAF's CIUS layer rejects it. You need a validator that checks the RO_CIUS business rules, not just the schema.
How do I read a cryptic ANAF error code?
The fastest path is to run the invoice through a validator that translates the rule into plain language instead of echoing a rule ID. A raw message like a bare rule reference tells you nothing; what you want is "line 4's VAT category is 'exempt' but there's no exemption reason code" — the what and the where.
You can paste an invoice into the free Fiscura validator (fiscura.ro) and get exactly that — every RO_CIUS / EN 16931 error explained in plain RO/EN, no key and no card. (Full disclosure: I build Fiscura. The validator is genuinely free and the explanations are the whole point of this article.)
What about the certificate + OAuth + SPV part?
This is the other half of the pain, and it's orthogonal to your XML:
- You authenticate to ANAF's SPV with a qualified digital certificate.
- You manage an OAuth token lifecycle (obtain, refresh, handle expiry mid-batch).
- You submit the invoice, get an upload index, and then poll for the processing verdict — acceptance isn't synchronous.
None of that is business logic you want to own. It changes, it's under-documented, and a single expired token silently fails a batch.
Can I skip building all of this?
Yes — that's the shortcut. Instead of hand-rolling SOAP + cert + polling, it's one REST call, or a few lines with the SDK:
npm i @gaperi/fiscura-sdk
The API covers the full path: validate RO_CIUS, generate compliant UBL 2.1, submit to ANAF SPV (using your own ANAF cert), convert UBL↔CII, and even extract a PDF invoice into structured JSON. There's a free tier (100 calls/day, no card), a self-serve developer account, and a quickstart in the docs. It's EU-hosted (Frankfurt), so it's GDPR-clean.
Want to see it run before you sign up? Clone the examples — a live, no-signup validation call in seconds (curl + plain-fetch JS + the SDK): github.com/gaperi-consult/fiscura-examples.
The short version
- Classify the rejection: RO_CIUS validation · missing field · cert/OAuth · SPV state.
- Your XML being schema-valid doesn't mean it's RO_CIUS-valid — check the business rules.
- Read errors in plain language, not rule IDs — paste one into the free validator.
- Don't hand-build the ANAF cert/OAuth/SPV lifecycle unless you have to.
Informational developer tooling, not tax advice. If you integrate e-Factura, I'd love to hear which ANAF error wasted the most of your time.
Top comments (0)