oneapi
Sign in
Anaf v1 / Utilitare cross-zonă

e-Invoice ↔ SAF-T reconciliation

Compares the invoices you issued against what appears in the SAF-T file and shows the mismatches both ways: invoices missing from the D406 and D406 entries with no invoice.

POST
https://oneapi.ro/api/anaf/v1/reconciliere-facturi

Request body

Sent as JSON in the request body (Content-Type: application/json) — not in the query string.

NameTypeDescriptionAccepted values
saftrequired string The SAF-T file (D406) to reconcile against, raw XML or base64.
facturirequired string[] | object[] The e-Invoices to reconcile, at most 5,000. An item is either the XML itself (text or base64) or the object {id, xml}. With id, the rows in lipsa_in_saft, diferente_valoare and numere_duplicate carry your identifier. An item that is neither XML nor {id, xml} returns 422 with its position.
SAF-T with an unsent invoice Request body
{
    "saft": "@saft-d406.xml",
    "facturi": [
        "@efactura.xml"
    ]
}

In this example, @saft-d406.xml, @efactura.xml stand in for whole documents: send the file content — XML as text or base64, a PDF as base64 — not its name.

Two invoices against the SAF-T file Request body
{
    "saft": "@saft-d406.xml",
    "facturi": [
        "@efactura.xml",
        "@efactura-stornare.xml"
    ]
}

In this example, @saft-d406.xml, @efactura.xml, @efactura-stornare.xml stand in for whole documents: send the file content — XML as text or base64, a PDF as base64 — not its name.

A reversal only Request body
{
    "saft": "@saft-d406.xml",
    "facturi": [
        "@efactura-stornare.xml"
    ]
}

In this example, @saft-d406.xml, @efactura-stornare.xml stand in for whole documents: send the file content — XML as text or base64, a PDF as base64 — not its name.

Query parameters

This route accepts no query parameters.

How it behaves

  • Compares the issued invoices (e-Invoice XMLs) against the sales invoices reported in the SAF-T file (D406, SourceDocuments/SalesInvoices), by invoice number. Catches what does not line up between the two reports: invoices present on one side but not the other, and totals (VAT-inclusive) that differ.
  • JSON body: {saft: "<xml>", facturi: ["<xml>", ...]} (raw XML or base64). Matched by number, and amounts are compared in whole bani (no floating-point pitfalls). On the e-Invoice side we read TaxInclusiveAmount (BT-112), which carries the same meaning as SAF-T's GrossTotal — not PayableAmount, which subtracts prepayments and would produce false mismatches.
  • If the same number appears twice on the same side (typically an invoice and a credit note sharing a number), the row is reported under numere_duplicate instead of being silently overwritten.
  • Zero retention and no SPV — everything is in-memory; nothing is submitted.

What it is for

  • Check that the SAF-T for the month contains exactly the invoices issued via e-Invoice, with the same amounts, before filing.
  • Quickly find the missing invoice or the wrong amount among hundreds of documents.

Response structure

FieldTypeDescription
data.are_diferente boolean true if there is at least one mismatch.
data.sumar object Counts: facturi_efactura, facturi_saft, lipsa_in_saft, lipsa_in_efactura, diferente_valoare, numere_duplicate.
data.sumar.facturi_efactura integer
data.sumar.facturi_saft integer
data.sumar.lipsa_in_saft integer
data.sumar.lipsa_in_efactura integer
data.sumar.diferente_valoare integer
data.sumar.numere_duplicate integer
data.lipsa_in_saft array Issued invoices (e-Invoice) missing from SAF-T [{id?,numar,tip,total}]; id appears only if you sent one.
data.lipsa_in_efactura[] array SAF-T invoices without a matching e-Invoice [{numar,total}].
data.lipsa_in_efactura[].numar string Number of items counted.
data.lipsa_in_efactura[].total integer
data.diferente_valoare array Matched invoices with a different total [{id?,numar,efactura,saft,diferenta}].
data.numere_duplicate array Numbers appearing more than once on the same side [{id?,numar,sursa,tip}].
meta.generat_la string When the response was built (ISO 8601).
meta.request_id string The identifier of this request. Quote it when reporting a problem — it is what the answer is found by.

Response examples

body: {"saft": "@saft-d406.xml", "facturi": ["@efactura.xml"]}

POSThttps://oneapi.ro/api/anaf/v1/reconciliere-facturi
{
    "data": {
        "are_diferente": true,
        "sumar": {
            "facturi_efactura": 1,
            "facturi_saft": 2,
            "lipsa_in_saft": 0,
            "lipsa_in_efactura": 1,
            "diferente_valoare": 0,
            "numere_duplicate": 0
        },
        "lipsa_in_saft": [],
        "lipsa_in_efactura": [
            {
                "numar": "FCT-2026-0143",
                "total": 1210
            }
        ],
        "diferente_valoare": [],
        "numere_duplicate": []
    },
    "meta": {
        "generat_la": "2026-10-06T23: 59: 41+03: 00",
        "request_id": "01a11303-d87c-724c-9897-cd8c69f79a4e"
    }
}

Error codes

CodeSlugWhen it occurs
401neautentificatToken missing, invalid, expired or revoked.
403feature_indisponibilThe current plan does not include the feature the route depends on.
422xml_invalidMissing saft or facturi, or a document is not valid XML; over 5000 invoices.
429cota_depasitaThe 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": "xml_invalid",
        "mesaj": "Missing saft or facturi, or a document is not valid XML; over 5000 invoices.",
        "camp": null,
        "request_id": "0199c1f0-6e1a-7c3d-9b2f-4a8e5d1c7b04"
    }
}