Anaf v1 / SAF-T (D406)
Validate D406
The official SAF-T XSD's verdict on a D406, plus our own checks that catch what the schema lets through — inconsistent periods and totals that do not add up.
POST
https://oneapi.ro/api/anaf/v1/saft/validate
Request body
The request body is the whole document, sent raw (Content-Type: application/xml) — not an object with fields.
D406 return
A quarterly D406
An annual D406
Query parameters
This route accepts no query parameters.
How it behaves
- The document can also be sent as a file:
multipart/form-datawith thefisierfield (curl -F fisier=@document.xml), instead of the raw body. The file content becomes the request body, so the result is identical; options stay in the query string, and any other form field next tofisierreturns422 camp_necunoscut. - Takes the standard fiscal control file (D406/SAF-T) in the request body and returns the validation verdict: (1) structural — the official ANAF XSD (v2.4.9), validated natively; (2) own checks — root
<AuditFile>, presence of<Header>and journal balance (Σ debit = Σ credit). - SAF-T has no public Schematron — the official business rules live in the ANAF validator (DUKIntegrator, a jar) which we do not reimplement; the XSD + own checks catch the bulk of structural and arithmetic errors before filing.
- An invalid XML is not a request error: the response is
200withvalid:falseand the list of errors inerrors[], each withcod,mesaj,camp. - Zero retention: the file received is not written and not logged.
What it is for
- Checking a D406 generated by accounting software, before filing it in SPV.
- Catching an unbalanced journal (debit ≠ credit) before ANAF rejects the file.
Response structure
FieldTypeDescription
data.valid
boolean
The cumulative verdict (XSD + own checks).
data.standard
string
D406.
data.schema_version
string
The schema/standard used for validation, when available (e.g.
FACT1 or d390_v3.xsd); otherwise null.
data.xsd_checked
boolean
Whether XSD validation ran.
data.errors
array
The validation errors
{cod,mesaj,camp}. Empty when valid is true.
data.warnings
array
Non-blocking warnings (e.g. unbalanced journal).
meta.generat_la
string
When the response was built (ISO 8601).
Response examples
body: @saft-d406.xml
POSThttps://oneapi.ro/api/anaf/v1/saft/validate
{
"data": {
"valid": true,
"standard": "D406",
"schema_version": "RO SAF-T v2.4.9 (2025)",
"xsd_checked": true,
"errors": [],
"warnings": []
},
"meta": {
"generat_la": "2026-10-06T23: 59: 44+03: 00"
}
}
body: @saft-d406-trimestrial.xml
POSThttps://oneapi.ro/api/anaf/v1/saft/validate
{
"data": {
"valid": true,
"standard": "D406",
"schema_version": "RO SAF-T v2.4.9 (2025)",
"xsd_checked": true,
"errors": [],
"warnings": []
},
"meta": {
"generat_la": "2026-10-06T23: 59: 44+03: 00"
}
}
body: @saft-d406-anual.xml
POSThttps://oneapi.ro/api/anaf/v1/saft/validate
{
"data": {
"valid": true,
"standard": "D406",
"schema_version": "RO SAF-T v2.4.9 (2025)",
"xsd_checked": true,
"errors": [],
"warnings": []
},
"meta": {
"generat_la": "2026-10-06T23: 59: 44+03: 00"
}
}
Error codes
| Code | Slug | When it occurs |
|---|---|---|
| 401 | neautentificat | Token missing, invalid, expired or revoked. |
| 403 | feature_indisponibil | The current plan does not include the feature the route depends on. |
| 413 | payload_prea_mare | The document exceeds the accepted size limit. |
| 422 | parametru_necunoscut | A query parameter the route does not accept. |
| 429 | cota_depasita | The plan's monthly quota has been exceeded (see Retry-After). |
Errors in the Anaf module use the same envelope as the rest of the platform, with two extra fields: {eroare: {cod, mesaj, camp, request_id}}. eroare.cod is stable and is what you branch on; on validation (422) the code is case-specific: xml_invalid, xml_nu_e_factura, unitate_necunoscuta, luna_invalida and so on. camp points at the offending field when one can be identified.
{
"eroare": {
"cod": "parametru_necunoscut",
"mesaj": "A query parameter the route does not accept.",
"camp": null,
"request_id": "0199c1f0-6e1a-7c3d-9b2f-4a8e5d1c7b04"
}
}