Skip to main content

Romania Fiscalization Guide

What is Romanian fiscalization?

Romania operates e-Factura, run by ANAF (Agenția Națională de Administrare Fiscală — National Tax Administration Agency). All B2B and B2G invoices must be transmitted as UBL 2.1 XML (RO_CIUS profile) to ANAF; B2C invoices became mandatory from 2025-01-01. ANAF acknowledges submissions asynchronously.

In plain English: Romania doesn't sign anything for you, but every business invoice has to go to ANAF in a specific UBL format. The OAuth2 dance with the qualified eIDAS certificate happens once per merchant; afterwards Zyntem submits invoices automatically.

You don't need to worry about this

Zyntem builds the UBL 2.1 RO_CIUS XML, manages the OAuth2 token rotation (eIDAS-backed authorization code flow + 1-year refresh tokens), submits to ANAF, polls for acceptance, and surfaces the final ANAF message id.

How it works

Romania's flow is asynchronous and OAuth2-protected:

  1. Your POS creates a transaction via POST /v1/transactions.
  2. Zyntem assembles the UBL 2.1 + RO_CIUS XML (CountryCode RO, CustomizationID carrying the RO_CIUS narrowing).
  3. Zyntem refreshes the access token if needed (POST /token with the rotating refresh token), then uploads via POST /upload.
  4. ANAF returns an index_incarcare (load index) — Zyntem stores this as fiscal_id.
  5. The transaction returns immediately with status: pending_submission.
  6. The polling worker hits /stareMesaj until ANAF reports OK or NEACCEPTAT. The final ANAF message id is stored in fiscal_id_upstream and the status flips to success (or failed_permanent for NEACCEPTAT).

Submission window: 5 calendar days from invoice date for B2B (Article 319 of the VAT Code). Zyntem queues immediately and respects ANAF rate limits.

One-time setup: OAuth2 + qualified certificate

Romania requires a qualified eIDAS certificate to authorize the integration. The certificate material never leaves the merchant's browser during onboarding — Zyntem captures only the resulting OAuth2 refresh token.

  1. Register Zyntem as an OAuth2 client with ANAF (logincert.anaf.ro).
  2. Direct the merchant to ANAF's /authorize endpoint via the front-channel — the merchant authenticates with their qualified certificate.
  3. ANAF redirects to your registered oauth_redirect_uri with an authorization code.
  4. Exchange the code for an initial refresh token (good for ~1 year).
  5. Configure the location with oauth_client_id, oauth_client_secret, oauth_redirect_uri, and the initial oauth_refresh_token. Zyntem rotates the refresh token on every /token call automatically.

Country config fields

The country_config object for Romanian locations:

FieldTypeRequiredDescription
cifstringYes2–10 digit issuer tax id, no RO prefix.
oauth_client_idstringYesOAuth2 client id from ANAF developer programme.
oauth_client_secretstringYesOAuth2 client secret.
oauth_redirect_uristring (HTTPS)YesPre-registered HTTPS redirect URI for the front-channel /authorize.
oauth_refresh_tokenstringYesLong-lived refresh token (~1 year, rotates on every refresh).
vat_group_representativestringNoCIF of the VAT-group representative (regim grup fiscal).
high_risk_productsbooleanNoTrue if the issuer trades products on the high-fiscal-risk list.

Configuration example

curl -X POST https://api.zyntem.dev/v1/locations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"name": "Bucharest Office",
"country": "RO",
"address": "Calea Victoriei 1, 010071 București",
"country_config": {
"cif": "12345678",
"oauth_client_id": "anaf-client-prod",
"oauth_client_secret": "<sealed>",
"oauth_redirect_uri": "https://callback.zyntem.io/ro/anaf",
"oauth_refresh_token": "<from /authorize round-trip>"
}
}'

Creating a transaction

Example: A B2B invoice for RON 1 200 to a Romanian customer.

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_abc123",
"type": "sale",
"amount": 120000,
"currency": "RON",
"buyer": {
"name": "Cumpărător SRL",
"tax_id": "RO87654321",
"country": "RO"
},
"line_items": [
{
"description": "Consultanță IT",
"quantity": 1,
"unit_price": 100840,
"vat_rate": 1900
}
]
}'

Response

{
"id": "txn_abc123",
"status": "pending_submission",
"fiscal_id": "1234567890",
"fiscal_id_upstream": null,
"country": "RO",
"created_at": "2026-03-15T14:30:22Z"
}

The fiscal_id is the ANAF index_incarcare (synchronous load index). The fiscal_id_upstream is null until ANAF finalizes; subscribe to transaction.fiscalized or poll GET /v1/transactions/{id} to see when it populates.

Sandbox access

ANAF test environment is at api.anaf.ro/test/FCTEL/rest. Sandbox routing is automatic with a test API key. Sandbox onboarding requires a separate test client registration with ANAF — typically 1 week.

Error handling

When ANAF rejects an invoice, error.category distinguishes:

  • transient — network blip, ANAF 5xx, OAuth2 token rotation in flight. Zyntem retries automatically.
  • regulatory_rejectNEACCEPTAT status with error.authority_code carrying ANAF's business-validation code. Correct the invoice and re-issue.
  • permanent — UBL schema mismatch or sealed-config error. Surface to operator.

See the Error handling guide for the full taxonomy.