Docs
12

Mapping versions

How an ERP's field names become our invoice, and how versions change.

  • In the sandbox

In plain words

A mapping translates one ERP's field names into our invoice model, which is how an error can name the field in the client's own export. When a client's field names change we add a new version and keep the old one, so we can go back. Business Central and SAP Business One are mapped, each from the ERP's published shape and not yet from a client's own export.

A mapping turns one ERP export into our canonical invoice. When a client's field names change, we add a new version and move the live pointer. The old version stays, so the pointer can move back.

What exists today

Two ERPs are mapped: Business Central (the API v2.0 sales invoice) and SAP Business One (an invoice or credit memo as the Service Layer returns it). Each has a live version, and older versions stay.

Note

Both mappings were written from the ERP's published shape and tested on invented exports, because no client export exists yet. A client whose export uses other names gets a new version. Nothing here asks the client to change the ERP.

An export goes to POST /validate or POST /invoices, with connector and export in place of document.

Send an export

POST /validate takes connector (business-central or sap-b1) and export (the export object) in place of document; POST /invoices takes the same two fields. Fields the mapping does not know are ignored. The report says which version ran, in mapping_version. The request is validate-export-ok.json, an export with invented parties.

curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  --data-binary @validate-export-ok.json
Response200 OK
{
  "valid": true,
  "route": "DE-XRECHNUNG",
  "documents": [
    {
      "sha256": "3e7f648bdc6c6d6f5ad78cd356b39c3020595bb7f0896b78a8510ec6969db317",
      "kind": "xrechnung-ubl",
      "content_base64": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgi... (5,900 characters, shortened for these docs)"
    }
  ],
  "layers": [
    {
      "findings": [],
      "passed": true,
      "layer": "schema"
    },
    {
      "findings": [],
      "passed": true,
      "layer": "mapping"
    },
    {
      "findings": [],
      "passed": true,
      "layer": "pre-check"
    },
    {
      "findings": [],
      "passed": true,
      "layer": "kosit"
    }
  ],
  "mapping_version": "v2"
}
Recorded on 7 Oct 2026. An invented invoice, through the live version.

Try another version

mapping_version in the request picks a version for that call. The pointer does not move. A version that does not exist answers 400. The request is validate-export-bad-version.json, the same export with "mapping_version": "v9".

curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  --data-binary @validate-export-bad-version.json
Response400 Bad Request
{
  "detail": "That mapping version does not exist. The live pointer was left unchanged.",
  "type": "https://eurinvoice.com/problems/bad-request",
  "title": "The request could not be read",
  "status": 400
}
Recorded on 7 Oct 2026.

Find the field in the ERP

A finding names the ERP's field, not our canonical one. A unit the mapping does not know is ours to fix: we add it to the unit table and cut a new version. The request is validate-export-bad-unit.json, where Kiste is not in the table.

curl -X POST "https://api-sandbox-eu.eurinvoice.com/validate" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  --data-binary @validate-export-bad-unit.json
Response200 OK
{
  "valid": false,
  "route": "DE-XRECHNUNG",
  "layers": [
    {
      "findings": [],
      "passed": true,
      "layer": "schema"
    },
    {
      "findings": [
        {
          "code": "BR-CL-23",
          "field": "salesInvoiceLines[0].unitOfMeasureCode",
          "fix_hint": "Add the ERP's unit to the client's unit mapping table (for example 'Std.' to HUR).",
          "who_fixes": "us",
          "source": "mapping",
          "message": "A unit of measure on a line is not recognised. We are adding it to your mapping."
        }
      ],
      "passed": false,
      "layer": "mapping"
    },
    {
      "findings": [],
      "passed": true,
      "layer": "pre-check"
    }
  ],
  "mapping_version": "v2"
}
Recorded on 7 Oct 2026.

Missing data is the partner's to fix. Its finding has who_fixes set to erp and names the ERP field to complete.

How a change ships

  1. We copy the live version to a new version and change it. The old one stays.
  2. We send the client's sample exports with mapping_version set to the new version, while the old one stays live.
  3. We move the live pointer to the new version.
  4. If something is wrong, we move it back.

On this page