ANAF compliance · D declarations · the full suite

You build and validate the D declarations, in one call.

You build a D declaration’s XML from a business JSON — with the totals calculated for you —, validate it against the official ANAF XSD for that form, read it back as JSON and render it as a PDF. One API parameterised by form, without storing anything.

The API is a registry of forms, and it is extensible: 13 D tax declarations are live today — from the VAT returns (D300, D301, D311, D390, D394) to taxes and income (D101, D205, D212), payroll (D112), notifications (D100, D700, D710) and the redirection of 3.5% of income tax (D230). D390 (the VIES recapitulative statement) is only one example. Processing happens in memory; filing in SPV stays with you.

A JSON in, the XML and the totals outPOST · in memory · zero retention
POST /declaratii/v1/D390/build
// Content-Type: application/json

{ "luna": 7, "an": 2026, "rectificativa": false,
  "declarant": { "cui": "RO12345678", "den": "ACME SRL" },
  "operatiuni": [
    { "tip": "L", "tara": "DE", "cod": "DE811234567", "baza": 18400 },
    { "tip": "A", "tara": "FR", "cod": "FR40303265045", "baza": 9250 } ] }

// → { formular: "D390", xml: "<Declaratie390 …>",
//     totaluri: { livrari_L: 18400, achizitii_A: 9250, nr_randuri: 2 } }

GET /declaratii/v1/forms   // the registry of supported forms

{ "forms": [
  { "cod": "D390", "nume": "Recapitulativă VIES", "status": "live" },
  { "cod": "D300", "nume": "Decont TVA",          "status": "live" },
  { "cod": "D394", "nume": "Informativă",         "status": "live" }
  // … D100, D101, D112, D205, D212, D301, D700, D710 — all live ] }
What you get One POST call, one business JSON, one valid XML with the totals calculated. No SPV account held by us, no file written to disk. Filing D390 in SPV stays with you, using your own credentials.
Forms
13 D declarations (D390 VIES…)
Routes
build · validate · parse · render
Registry
/forms · /codes/{list}
XSD
official ANAF, per form
Retention
none
01 The problem

A correct D declaration is more than an XML. And it is not your product.

Between the data in your accounts and a D390 that ANAF accepts at filing sits a layer of rules — XSD structure, totals by operation type, validated partner codes — that someone has to build and check.

A

The form, as XML

D390 is not a web form, it is an XML with a strict structure: a header with the period and the declarant, one row per intra-community partner, with operation type, country, VAT code and base. Order and types matter.

B

Totals by type

The recapitulative statement wants amounts grouped by operation type — supplies, acquisitions, services, triangular. A wrong sum or a row filed under the wrong type, and the declaration does not match the VAT return.

C

Partner codes

Every row carries an intra-community VAT code, with a country prefix. An invalid code, or one that does not match the country, slips past a human but fails at validation — and often you only find out at filing.

D

The official schema

The structure is checked against the official ANAF XSD for the form. A missing element, a wrong type, a broken cardinality, and the file is rejected.

E

Corrective filings

A declaration filed wrong is corrected with a corrective filing, with its own marking rules. It has to be built as rigorously as the original, not improvised.

F

Rules that change

The schemas get revised, forms are added and amended. What passed last year can fail today, and you find out from the rejection, not from an announcement.

None of this sets you apartYour customer does not pay you because you know the structure of a D390 or because you group the amounts by operation type correctly. They pay you for your product. The layer that builds and checks the declarations is pure cost — written once and maintained for every new form — and it is exactly what we move onto our side.
02 What it does

Four operations and a registry of forms.

Each is an independent POST call, parameterised by form. You build, validate, read and render — and list the supported forms. You use the one you need.

POST /{form}/build

Build

You send a business JSON and get the declaration XML. The totals by operation type are calculated from the rows, you do not carry them yourself. You come out with a file valid against the form’s schema.

POST /{form}/validate

Validate

You send an existing XML and get the verdict. We check it against the official ANAF XSD for the form plus checks of our own — header, totals, partner codes. You get what was checked, the errors and the warnings.

POST /{form}/parse

Read

A declaration XML becomes clean JSON: the header (declarant, period, corrective flag) and the rows, with the totals by type. You read it without touching the XML.

POST /{form}/render

Render

From the declaration comes a human-readable PDF, generated locally — the header, the rows and the totals by type, for the file or for a review, without an external rendering service.

GET /forms

Form registry

The list of supported forms, with the status of each — all 13 D declarations live today, with the status read from GET /forms. One place to find out what you can build, without guessing.

GET /codes/{list}

Code lists

The official lists the forms use — country codes, operation types — served as a reference, so you can check from the form before building.

03 Zero retention

Your tax data goes nowhere. Not to us, not to ANAF.

This is the answer to “why would I send my tax data to a third party”. Because, in practice, you are not sending it anywhere — we process and forget.

01

Processed in memory

The JSON and the XML are never written to disk. They arrive in the request, are built, validated or read in memory and are gone when the response leaves. There is no declaration of yours at our end.

02

Local validation

The official XSD and our own checks run on our side, on the server holding your response. We do not send the declaration to ANAF to find out whether it is valid — the rules are here, the verdict is local.

03

Logs without content

What we keep is a result code and a duration — that is all. The VAT number, the partners, the amounts, the VAT codes go into no log, neither ours nor an observability service’s.

04

The result only

An optional header tells us to return only the verdict or the totals, without the rows in the clear. For integrations where you want the build or the validation but would rather the data did not travel back through the response.

Why it matters to youA D declaration holds tax data: VAT number, intra-community partners, amounts, VAT codes. A service that stores it becomes a target and a liability. We chose the version where there is nothing to steal: a stateless tool that builds, validates and reads and does not remember.
04 Official validation

We do not just say “invalid”. We say exactly what and where.

We run the form’s official ANAF XSD locally, plus checks of our own, and give you the diagnosis before filing — by field and by row.

The official XSD schema

We check the structure against the official ANAF XSD for the form: elements, types, order, cardinalities. If a mandatory field is missing or a type does not match, we catch it before it ever reaches SPV.

Our own checks

On top of the XSD we run our own checks: a complete and coherent header, the totals by operation type, VAT codes with a valid country prefix. The things that pass the schema and still make the declaration wrong.

Diagnosis, not just a verdict

The response tells you what was checked, whether the declaration is valid, plus the list of errors and warnings with the rule and the row involved. The difference between knowing there is a problem and knowing where it is.

No filing in SPV

Validation is entirely local: by default, nothing leaves us for ANAF. Filing D390 through SPV stays with you, using your own credentials — we give you the verdict and the totals before that step.

05 What we cover

Thirteen D declarations, all live, on the same pattern.

The whole suite of D forms live now, each with build, validate, parse and render, and with the totals calculated for you.

VAT · D300, D301, D311, D390, D394 Income/profit · D101, D205, D212 Payroll · D112 Payments/notifications · D100, D700, D710 3.5% redirection · D230 build · validate · parse · render Official ANAF XSD, per form Corrective filings Official code lists

We cover the complete suite of D declarations — from the VAT returns to the unified return —, each with its official XSD, totals and corrective filings. We add new forms on the same pattern — if you need one that is not live yet, write to us — real cases take priority in what we build.

06 Integration

A business JSON in. Valid XML and the totals out.

You send the JSON and get the XML with the calculated totals — or, on validate, the verdict. No mandatory SDK — the HTTP client you already have.

POST/api/anaf/v1/declaratii/D390/buildapplication/json
# build: a business JSON → valid D390 XML + totals calculated for you
curl "https://oneapi.ro/api/anaf/v1/declaratii/D390/build" \
  -H "Authorization: Bearer $ONEAPI_TOKEN" \
  -H "Content-Type: application/json" \
  -d @d390_2026_07.json

# → 200 OK
{
  "formular": "D390",
  "xml":      "<Declaratie390 …>",
  "totaluri": { "livrari_L": 18400, "achizitii_A": 9250, "nr_randuri": 2 }
}
When validation fails

What was checked, the rule and the row

A validation response does not just say “invalid”. It tells you what was checked — the form’s XSD and our own checks — whether it is valid, plus the errors and warnings with the rule and the row involved. Enough to fix it first time instead of guessing.

HTTP/1.1 422 Unprocessable Content
{ "valid": false,
  "formular":    "D390",
  "xsd_checked": "v3.0",
  "errors": [ {
    "regula": "D390-COD-01",
    "mesaj":  "Cod TVA partener invalid pentru țara DE.",
    "camp":   "operatiuni[0].cod"
} ], "warnings": [] }
07 Questions

Before you file the first D390.

The rest, in the frequently asked questions and in the documentation.

Yes — that is the difference from SAF-T. You send a business JSON and get the declaration XML, with the totals by operation type calculated for you. You bring the data from your accounts; we group it, structure it and validate it against the form’s schema. Filing in SPV stays with you.

The first D390 takes as long as reading this page. One token, one business JSON, valid XML and the totals back. Zero retention, from the first call.
Generate a token All modules