You validate and read the SAF-T file, in one call.
Check a SAF-T (D406) file against the official ANAF XSD (v2.4.9) plus checks of our own, read it as JSON with a per-section summary and render it as a PDF — all through one API, without storing anything.
We do not build the declaration from scratch — the file is produced by your ERP or accounting software. We validate it, unpack it and give you the verdict plus the summary. Processing happens in memory; the content never touches a disk of ours.
// multipart: fisier=D406.xml
// → { valid: true, standard: "D406", xsd_checked: "v2.4.9",
// errors: [], warnings: [] }
POST /saft/v1/parse // the same file, unpacked
{
"header": { "firma": "RO12345678", "perioada": "2026-07", "versiune": "2.4.9" },
"rezumat": {
"master_files": { "conturi": 412, "clienti": 1380, "furnizori": 210 },
"jurnal": { "nr_jurnale": 8, "total_debit": 4820140.55, "total_credit": 4820140.55 }
}
}
- Standard
- D406 SAF-T
- Namespace
- mfp:anaf:dgti:d406t
- Official XSD
- ANAF v2.4.9
- Operations
- validate · parse · render (no build)
- Retention
- none
A valid SAF-T file is hard to check. And it is not your product.
Between the file your ERP exports and a D406 that ANAF accepts at filing sits a layer of rules — schema, structure, accounting balance — that someone has to check beforehand.
An enormous file
SAF-T is not a form, it is an XML with an AuditFile root and the namespace mfp:anaf:dgti:d406t:declaratie:v1 — master data, the whole accounting journal and the source documents. Tens of thousands of lines, with strict structure and order.
The official v2.4.9 schema
The structure is checked against the official ANAF XSD, version 2.4.9. A missing element, a wrong type, a muddled order, and the file is rejected at validation.
Master files
The chart of accounts, customers, suppliers, the tax table, products, stock, fixed assets. Each section has its own mandatory fields and codes from the official lists.
Accounting journal
The complete general ledger, with every accounting entry. The debit=credit balance has to close — one difference, however small, and the declaration is wrong.
Source documents
Sales and purchase invoices, payments, stock movements. Linked to master files and to the journal, with coherence between totals and entries.
Rules that change
The schema gets revised, versions succeed one another (v2.4.9 today), the structure gets adjusted. What passed last year can fail today, and you find out from the rejection, not from an announcement.
Three operations and a code list. No generation.
Each is an independent POST call. We do not build the file — we validate it, read it and render it. You use the one you need.
/validate
Validate
You send an existing SAF-T file and get the verdict. We check it against the official ANAF XSD v2.4.9 plus checks of our own — the AuditFile root, the header, the debit=credit balance of the journal. You get what was checked, the errors and the warnings.
/parse
Read
A SAF-T file becomes clean JSON: the header (company, period, currency, software, version) and a per-section summary — how many accounts, customers, suppliers, products, how many journals with total debit and credit, how many documents. You read it without touching the XML.
/render
Render
From the file comes a human-readable summary PDF, generated locally. The header and per-section totals, for the file or for a review, without an external rendering service.
/codes
Code lists
The official lists D406 uses — tax codes, account types, units of measure — served as a reference, so you can check from the form before generating.
Your accounting goes nowhere. Not to us, not to ANAF.
This is the answer to “why would I send my whole ledger to a third party”. Because, in practice, you are not sending it anywhere — we process and forget.
Processed in memory
The file is never written to disk. It arrives in the request, is validated and read in memory and is gone when the response leaves. There is no SAF-T file of yours at our end.
Local validation
The official v2.4.9 XSD and our own checks run on our side, on the server holding your response. We do not send the file to ANAF to find out whether it is valid — the rules are here, the verdict is local.
Logs without content
What we keep is a result code and a duration — that is all. The VAT number, the accounts, the partners, the amounts go into no log, neither ours nor an observability service’s.
The verdict only
An optional header tells us to return only the verdict and the summary, with nothing from the content of the lines. For integrations where you want the validation but would rather the data did not travel back through the response.
We do not just say “invalid”. We say exactly what and where.
We run the official v2.4.9 XSD in full, locally, plus checks of our own, and give you the diagnosis the DUK Integrator validator does not state as clearly before filing.
Official v2.4.9 XSD schema
We check the structure against the official ANAF XSD, version 2.4.9: 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: the AuditFile root and the correct namespace, a complete and coherent header, the debit=credit balance of the accounting journal. The things that pass the schema and still make the file wrong.
Diagnosis, not just a verdict
The response tells you what was checked, whether the file is valid, plus the list of errors and warnings with the rule and the place 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 D406 through SPV stays with you, using your own credentials — we give you the verdict and the summary before that step.
From the chart of accounts to the source documents.
The real sections of a D406 file, not just the header. Each validated and read, with the totals counted.
We cover the complete D406 file, with master files, accounting journal and source documents. If you hit a section we do not handle correctly, write to us — real cases take priority in what we build.
A SAF-T file in. The verdict and the summary out.
You send the file and get the validation verdict or the JSON with header and summary. No mandatory SDK — the HTTP client you already have.
# validate: a D406 file → a verdict against the official v2.4.9 XSD + our own checks
curl "https://oneapi.ro/api/anaf/v1/saft/validate" \
-H "Authorization: Bearer $ONEAPI_TOKEN" \
-F "fisier=@d406_2026_07.xml"
# → 200 OK
{
"valid": true,
"standard": "D406",
"xsd_checked": "v2.4.9",
"errors": [],
"warnings": []
}
What was checked, the rule and the place
A validation response does not just say “invalid”. It tells you what was checked — the v2.4.9 XSD and our own checks — whether it is valid, plus the errors and warnings with the rule involved. Enough to fix it first time instead of guessing.
HTTP/1.1 422 Unprocessable Content
{ "valid": false,
"standard": "D406",
"xsd_checked": "v2.4.9",
"errors": [ {
"regula": "SAFT-BAL-01",
"mesaj": "Jurnal: debit ≠ credit (Δ 0.02).",
"camp": "GeneralLedgerEntries"
} ], "warnings": [] }
Before you file the first D406.
The rest, in the frequently asked questions and in the documentation.