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.
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:
- Your POS creates a transaction via
POST /v1/transactions. - Zyntem assembles the UBL 2.1 + RO_CIUS XML (CountryCode
RO,CustomizationIDcarrying the RO_CIUS narrowing). - Zyntem refreshes the access token if needed (
POST /tokenwith the rotating refresh token), then uploads viaPOST /upload. - ANAF returns an
index_incarcare(load index) — Zyntem stores this asfiscal_id. - The transaction returns immediately with
status: pending_submission. - The polling worker hits
/stareMesajuntil ANAF reportsOKorNEACCEPTAT. The final ANAF message id is stored infiscal_id_upstreamand the status flips tosuccess(orfailed_permanentforNEACCEPTAT).
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.
- Register Zyntem as an OAuth2 client with ANAF (
logincert.anaf.ro). - Direct the merchant to ANAF's
/authorizeendpoint via the front-channel — the merchant authenticates with their qualified certificate. - ANAF redirects to your registered
oauth_redirect_uriwith an authorization code. - Exchange the code for an initial refresh token (good for ~1 year).
- Configure the location with
oauth_client_id,oauth_client_secret,oauth_redirect_uri, and the initialoauth_refresh_token. Zyntem rotates the refresh token on every/tokencall automatically.
Country config fields
The country_config object for Romanian locations:
| Field | Type | Required | Description |
|---|---|---|---|
cif | string | Yes | 2–10 digit issuer tax id, no RO prefix. |
oauth_client_id | string | Yes | OAuth2 client id from ANAF developer programme. |
oauth_client_secret | string | Yes | OAuth2 client secret. |
oauth_redirect_uri | string (HTTPS) | Yes | Pre-registered HTTPS redirect URI for the front-channel /authorize. |
oauth_refresh_token | string | Yes | Long-lived refresh token (~1 year, rotates on every refresh). |
vat_group_representative | string | No | CIF of the VAT-group representative (regim grup fiscal). |
high_risk_products | boolean | No | True 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_reject—NEACCEPTATstatus witherror.authority_codecarrying 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.