Skip to content

Romania (ANAF)

Romania’s e-invoicing system is operated by ANAF (Agentia Nationala de Administrare Fiscala) through the e-Factura platform. Since January 2024, all B2B transactions between Romanian VAT-registered entities must be reported through e-Factura.

Mandato handles the entire ANAF integration: OAuth2 authentication, JSON-to-UBL conversion, XML submission, status polling, and error translation.

PropertyValue
Government systemANAF e-Factura
XML formatUBL 2.1 CIUS-RO
Profileurn:cen.eu:en16931:2017#compliant#urn:efactura.mfinante.ro:CIUS-RO:1.0.1
AuthenticationOAuth2 + digital certificate
Token lifetimeAccess token: 90 days, Refresh token: 365 days
Upload endpointPOST /upload/FACT1/{vatNumber}
Status endpointGET /listaMesajeFactura?cif={vatNumber}&zile=60

Before you can submit invoices to ANAF through Mandato, you need:

  1. A Romanian company with a valid CUI (Cod Unic de Identificare)
  2. An ANAF SPV account (Spatiul Privat Virtual) for the company
  3. A digital certificate — either a USB hardware token or a cloud-based certificate registered with ANAF
  4. A Mandato account with an API key
  1. Register your company in Mandato

    Create a company with your Romanian VAT details:

    import { MandatoClient } from "@getmandato/sdk";
    const mandato = new MandatoClient({
    apiKey: "sk_live_your_production_key",
    });
    const { data: company } = await mandato.companies.create({
    name: "TechVision SRL",
    vatNumber: "RO12345678",
    country: "RO",
    registrationNumber: "J40/1234/2020",
    address: {
    street: "Strada Exemplu 42",
    city: "Bucharest",
    postalCode: "010101",
    country: "RO",
    },
    currency: "RON",
    });
  2. Initialize the ANAF connection

    Create a connection for Romania. This returns an OAuth authorization URL.

    const { data: connection } = await mandato.connections.create({
    companyId: company.id,
    countryCode: "RO",
    });
    console.log("Authorize at:", connection.authorizationUrl);
    // https://logincert.anaf.ro/anaf-oauth2/v1/authorize?response_type=code&client_id=...&state=...
  3. Complete the OAuth flow

    Redirect the company administrator to the authorizationUrl. They will:

    1. Access the ANAF login page with their digital certificate inserted
    2. Authenticate using the certificate PIN
    3. Authorize Mandato to access e-Factura on behalf of the company
    4. Be redirected back to Mandato’s callback endpoint

    After the callback is processed, the connection status changes to active.

    // Poll until the connection is active
    const { data: activeConnection } = await mandato.connections.get(connection.id);
    console.log(activeConnection.status); // "active"
    console.log(activeConnection.tokenExpiresAt); // ~90 days from now
  4. Submit your first invoice

    Once the connection is active, you can submit invoices:

    const { data: invoice } = await mandato.invoices.create({
    country: "RO",
    companyId: company.id,
    supplier: {
    vatNumber: "RO12345678",
    name: "TechVision SRL",
    address: {
    street: "Strada Exemplu 42",
    city: "Bucharest",
    postalCode: "010101",
    country: "RO",
    },
    },
    customer: {
    vatNumber: "RO87654321",
    name: "Client Corp SRL",
    address: {
    street: "Bulevardul Unirii 10",
    city: "Cluj-Napoca",
    postalCode: "400001",
    country: "RO",
    },
    },
    lines: [
    {
    description: "Servicii dezvoltare software - Ianuarie 2025",
    quantity: 160,
    unitPrice: 50,
    vatRate: 19,
    unitCode: "HUR", // Hours
    },
    ],
    currency: "RON",
    issueDate: "2025-01-31",
    dueDate: "2025-02-28",
    paymentMeans: {
    bankAccount: "RO49AAAA1B31007593840000",
    bic: "AAABROBU",
    paymentTerms: "Net 30",
    },
    });

Romanian VAT numbers (CUI — Cod Unic de Identificare) follow this format:

FormatExampleNotes
RO + 2-10 digitsRO12345678Standard format, always include the RO prefix
Without prefix12345678Mandato also accepts this and prepends RO

The CUI is validated against ANAF’s records. In the sandbox, any correctly formatted CUI is accepted.

RateCodeDescription
19%SStandard rate (most goods and services)
9%SReduced rate (food, water, medicine, books, hotels)
5%SSuper-reduced rate (housing under certain conditions)
0%ZZero rate (exports, intra-community supplies)
ExemptEVAT exempt (financial, insurance, education, medical)

Set the vatRate on each invoice line to the numeric value (e.g., 19, 9, 5, or 0).

Mandato converts your JSON invoice to UBL 2.1 XML conforming to the CIUS-RO (Core Invoice Usage Specification for Romania) profile. The conversion handles:

  • EN 16931 base structure and rules
  • Romanian-specific business rules (BR-RO-xxx)
  • Correct XML namespaces and schema references
  • Romanian diacritics in party names and addresses
  • Proper decimal formatting for amounts

You do not need to understand or write XML. Mandato generates valid UBL from your JSON input.

Beyond the standard required fields, Romanian invoices need:

FieldRequirementNotes
Supplier VATRequiredMust be a valid Romanian CUI
Supplier addressRequiredStreet, city, postal code, and country
Customer VATRequiredRomanian CUI for B2B, or foreign VAT for exports
Customer addressRequiredStreet, city, postal code, and country
CurrencyRequiredRON for domestic, EUR/USD for international
Issue dateRequiredCannot be more than 5 days in the past

Mandato validates Romanian invoices against three rule sets:

  1. EN 16931 — EU base semantic rules (BR-01 through BR-65)
  2. CIUS-RO — Romanian-specific rules (BR-RO-001 through BR-RO-080)
  3. ANAF business rules — Additional rules enforced by the ANAF system

The POST /v1/invoices/validate endpoint runs all three rule sets without submitting.

When ANAF rejects an invoice, the raw error is in Romanian. Mandato translates these to English and suggests fixes.

ANAF errorMeaningFix
CUI invalidThe supplier or customer CUI is not registered in ANAFVerify the VAT number at ANAF online
XML invalid conform schemeiThe XML does not conform to the UBL schemaUsually a Mandato bug — contact support
Factura duplicataAn invoice with the same number was already submittedUse a different invoice number or check the externalId
Certificat digital expiratThe OAuth token or certificate has expiredRe-authorize the connection
Cod CAEN invalidInvalid activity code referenceCheck the item classification codes
Adresa fiscala incompletaIncomplete fiscal addressEnsure all address fields are provided (street, city, postal code)
Cota TVA invalidaInvalid VAT rateUse one of the valid Romanian rates: 19%, 9%, 5%, 0%

Every rejected invoice has three error-related fields:

{
"errorMessage": "CUI cumparator invalid - nu este inregistrat in scopuri de TVA",
"errorTranslated": "Customer VAT number is not registered for VAT purposes in Romania",
"errorFix": "Verify the customer's CUI at anaf.ro. If the customer is not VAT-registered, use a B2C invoice format instead."
}
  • errorMessage — Raw error from ANAF (in Romanian)
  • errorTranslated — AI-translated error in English
  • errorFix — AI-suggested action to resolve the issue

Mandato manages ANAF OAuth tokens automatically:

EventBehavior
Token nearing expiryAuto-refreshed using the refresh token
Refresh token expiredConnection status changes to expired, connection.expired webhook fires
Refresh failsRetried 3 times with backoff, then marked error
Manual re-auth neededCreate a new connection to get a fresh authorization URL

Check the tokenExpiresAt field on your connection:

const { data: connection } = await mandato.connections.get("conn_id");
const expiresAt = new Date(connection.tokenExpiresAt!);
const daysUntilExpiry = Math.floor(
(expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
);
console.log(`Token expires in ${daysUntilExpiry} days`);

Subscribe to the connection.expired webhook event to be notified when re-authorization is needed.

In the test environment (sk_test_ keys):

  • No real ANAF OAuth is needed — connections are auto-activated
  • Invoices are validated with the same CIUS-RO rules
  • XML is generated but not submitted to ANAF
  • The simulated acceptance takes ~10 seconds
  • Use RO12345678 and RO87654321 as test VAT numbers
// Sandbox testing -- no OAuth needed
const mandato = new MandatoClient({
apiKey: "sk_test_your_test_key",
});
const { data: company } = await mandato.companies.create({
name: "Test SRL",
vatNumber: "RO12345678",
country: "RO",
});
// Connection is auto-activated in sandbox
const { data: connection } = await mandato.connections.create({
companyId: company.id,
countryCode: "RO",
});
console.log(connection.status); // "active" immediately
EnvironmentBase URLUsage
ANAF Testhttps://api.anaf.ro/test/FCTEL/restANAF’s own sandbox (used by Mandato internally)
ANAF Productionhttps://api.anaf.ro/prod/FCTEL/restLive production system

You do not need to configure these URLs. Mandato routes to the correct ANAF environment based on your API key prefix (sk_test_ vs sk_live_).

Do I need a digital certificate for every invoice?

Section titled “Do I need a digital certificate for every invoice?”

No. The digital certificate is only needed once, during the initial OAuth2 authorization flow. After that, Mandato uses OAuth tokens that are automatically refreshed.

Yes. Create a separate company and connection for each CUI. Each company has its own OAuth tokens.

Mandato queues the invoice and retries submission automatically. The invoice stays in submitting status until ANAF responds. You are notified via webhook when the status changes.

Is there a delay between submission and acceptance?

Section titled “Is there a delay between submission and acceptance?”

Typically ANAF processes invoices within 1-15 minutes. During peak periods (end of month), processing can take up to several hours.

Yes. Set invoiceType: "credit_note" in the request body. Credit notes follow the same CIUS-RO rules with additional requirements for referencing the original invoice.