eliDocs
API Reference

POST /v1/convert

POST/v1/convert

Convert EN 16931-family e-invoices: CII ↔ UBL, ZUGFeRD ↔ Factur-X, XRechnung CII → UBL, Peppol BIS Billing 3.0 → EN 16931 UBL. The output XML is validated.

Convert an e-invoice from one EN 16931-family format to another in a single call.

The engine routes the conversion through these steps in order: extract embedded CII when the source is a hybrid PDF, convert between CII and UBL via Mustang (cii2ubl) or Helger en16931-ubl2cii when the syntaxes differ, rewrite the CustomizationID / guideline URN to match the target profile, and on a UBL to CII conversion write the source’s invoice total VAT amount (BT-110) back where the converter left it out. It then validates the XML it is about to return. A hybrid PDF sent for a zugferd or facturx target comes back as a PDF: re-wrapped around the converted XML unless its embedded XML already is the target.

The engine validates the XML it returns. A 200 means two things about the returned XML: it carries the guideline identifier of the target format and profile, and it passed the schemas and rule packs Beliq runs for a document with that identifier. That is the validation pipeline POST /v1/validate uses, always on the latest ruleset; validation artifacts lists the versions. A converted document that fails is not returned: the request answers 422 CONVERSION_OUTPUT_INVALID with the verdict in error.details.validationResult (see the details).

The 200 carries no verdict, in its body or in a header. Beliq stores the verdict with the job, and the job.completed webhook of a conversion carries its valid flag and its error and warning counts. To read the findings themselves, warnings included, send the converted document to POST /v1/validate.

A 200 says no more than that:

  • A PDF that comes back as you sent it is not checked as a PDF. Its embedded XML was validated; the PDF around it was not checked against PDF/A-3. A PDF the engine re-wrapped passed the PDF/A-3b profile of veraPDF 1.30.3 (how).
  • A Factur-X or ZUGFeRD EN 16931 output is judged as plain EN 16931 CII. That profile carries the plain EN 16931 identifier, so for a facturx or zugferd target at targetProfile=en16931 the output is validated as plain EN 16931 CII, with no Factur-X profile schema.
  • The CII ↔ UBL converters are not checked field by field. Mustang cii2ubl runs as published. Helger ubl2cii leaves an invoice total VAT amount (BT-110) of 0 out of the CII, so where the CII has no BT-110 in the invoice currency, Beliq writes in the one the UBL source states, as the source wrote it, and X-Conversion-Tools names bt110-restore. Beliq derives no value: unless the source states exactly one such total and the CII names the same invoice currency, nothing is written. Beyond BT-110, Beliq does not compare the business terms in the source with those in the output, so a field either tool leaves out would not be reported. Validating the output refuses such a loss only where a rule sees it.
  • A recipient can still refuse the document. The verdict is about the schemas and rule packs above. It does not mean that every recipient, validation tool or tax authority accepts the document.

Two losses are detected and fail closed with 422 CONVERSION_LOSSY_FAILCLOSED: a French cadre de facturation code (BT-23) on a UBL target (you can opt in to dropping it), and a Peppol target from a source that has no Peppol endpoints. Losses the engine treats as acceptable, such as a PDF envelope dropped for an XML target, are listed in the response headers.

What a conversion does with the identifier and the content:

  • A same-syntax conversion rewrites the identifier; the content is not adapted. Converting to another Factur-X or ZUGFeRD profile, or to XRechnung, rewrites the CustomizationID / guideline ID. Apart from a CII ↔ UBL conversion when the syntaxes differ, the content stays as it was, and the result is validated under the new identifier. A change the content does not fit therefore answers 422 CONVERSION_OUTPUT_INVALID: an EN 16931 invoice with lines converted to minimum or basicwl, two profiles that hold no invoice lines; a MINIMUM or BASIC WL document converted to en16931, which does not gain the lines it lacks; a plain invoice converted to xrechnung that does not meet the German (BR-DE) rules.
  • Some rewrites are refused. When the syntax stays the same, the plain EN 16931 identifier (the targets cii and ubl, and facturx or zugferd at en16931) is not written over a profile that extends EN 16931: a Factur-X EXTENDED or EXTENDED-CTC-FR source is refused, because the core rules do not see what an extension adds. No identifier is written over a source whose own identifier the engine does not name. It names the plain EN 16931 identifier, the XRechnung and Peppol BIS Billing 3.0 identifiers Beliq pins, and the Factur-X profile identifiers; the Extension XRechnung identifier and a ZUGFeRD 1.0 identifier are two it does not. A source that states no specification identifier (BT-24) is refused on every path. Each of the three answers 422 CONVERSION_UNSUPPORTED_PAIR with a reason in error.details. A conversion between CII and UBL is not held to the first two: its output gets the target’s identifier whatever profile the source declared.
  • A source that already is the target comes back unchanged. When the source already carries the target’s identifier in the target’s syntax, nothing is rewritten and no converter runs. It is still validated, so a source that does not pass answers 422 CONVERSION_OUTPUT_INVALID. targetProfile counts: a Factur-X BASIC document sent with targetFormat=facturx&targetProfile=minimum is not returned as BASIC. Its identifier is rewritten to MINIMUM and the result is validated as MINIMUM. An XRechnung CII document sent with targetFormat=xrechnung is returned as CII. A hybrid PDF comes back as sent only when its XML also resolves to the target format; otherwise it is re-wrapped around that same XML.

Out of scope. FatturaPA, Facturae, KSeF, and SDI messaggio sources or targets are not supported (no published EN 16931-equivalent converter; conversion would silently lose national overlay fields). Use /v1/generate to produce FatturaPA or Facturae from structured JSON instead.

Quick copy examples

Quick copy examples

Convert CII → UBL:

convert-cii-to-ubl.sh
curl -X POST "https://api.beliq.eu/v1/convert?targetFormat=ubl" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @invoice.cii.xml \
  --output invoice.ubl.xml

Convert a ZUGFeRD PDF → UBL XML (extracts the embedded CII; the PDF envelope is dropped and recorded as an acceptable loss):

convert-zugferd-pdf-to-ubl.sh
curl -X POST "https://api.beliq.eu/v1/convert?targetFormat=ubl" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/pdf" \
  --data-binary @zugferd.pdf \
  --output invoice.ubl.xml

Convert a Peppol BIS UBL → plain EN 16931 UBL (strips the cbc:EndpointID blocks; acceptable loss recorded):

convert-peppol-to-en16931.sh
curl -X POST "https://api.beliq.eu/v1/convert?targetFormat=ubl" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @peppol.xml \
  -D - \
  --output en16931.xml
# Response headers include X-Lost-Elements-Count: 2 (one per stripped endpoint).
Request

Request

POST /v1/convert
Content-Type: application/xml | application/pdf
Authorization: Bearer <api-key>

Headers

Header Required Description
Authorization required Bearer <api-key> or use X-API-Key header
Content-Type required application/xml for XML input, application/pdf (or application/octet-stream) for a hybrid ZUGFeRD/Factur-X PDF

Query parameters

Parameter Type Required Description
sourceFormat string optional One of: auto (default), cii, ubl, zugferd, facturx, xrechnung, peppol-bis. With auto the engine inspects the XML root + CustomizationID URN.
targetFormat string required One of: cii, ubl, zugferd, facturx, xrechnung, peppol-bis.
targetProfile string optional ZUGFeRD / Factur-X profile when targetFormat ∈ {zugferd, facturx}: minimum, basicwl, basic, en16931 (default), extended, extended-ctc-fr. The output carries that profile’s identifier. A CII source keeps its content and only the identifier is rewritten, so a result that does not pass under the profile is refused with 422 CONVERSION_OUTPUT_INVALID, and en16931 is not written over an EXTENDED or EXTENDED-CTC-FR CII source (422 CONVERSION_UNSUPPORTED_PAIR). Ignored for other targets.
dropFranceCtcOverlay boolean optional When true, the engine accepts the loss of a French cadre de facturation code (BT-23, any of the twenty XP Z12-012 values) on UBL targets and records it in the response as an acceptable loss. Default: false (fail-closed with 422 CONVERSION_LOSSY_FAILCLOSED).

A query parameter this table does not list answers 400 VALIDATION_ERROR, and error.details.fields names it as a JSON Pointer, such as /formt.

Request body

The raw XML or hybrid PDF file content. Do not JSON-encode it.

Supported pairs

Supported pairs

Source and target must both be in the EN 16931 family. Every row that returns a document validates its XML first (see above), so each of them can also end in 422 CONVERSION_OUTPUT_INVALID. The table summarises what runs:

Pair What runs Notes
CII ↔ UBL (any profile) Mustang cii2ubl (CII → UBL) or Helger ubl2cii (UBL → CII) + URN swap A converter JAR runs before the validation; the heavier path.
ZUGFeRD ↔ Factur-X, or a change of profile (same syntax) URN swap only (no converter) Only the profile URN changes. No content is added or removed, so a change the content does not fit answers 422 CONVERSION_OUTPUT_INVALID: an invoice with lines sent to minimum or basicwl, or a MINIMUM or BASIC WL document sent to en16931. A change to en16931 from EXTENDED or EXTENDED-CTC-FR is refused with 422 CONVERSION_UNSUPPORTED_PAIR. A source that already carries the target profile’s identifier is returned unchanged.
XRechnung-CII → XRechnung-UBL Mustang cii2ubl + URN swap, with sourceFormat=cii only With sourceFormat=auto the source resolves as xrechnung, which is already the target, so the CII document comes back unchanged. Declare sourceFormat=cii to get the UBL binding, which is validated under the XRechnung identifier.
Peppol BIS UBL → plain EN 16931 UBL URN swap + cbc:EndpointID strip Endpoints recorded as acceptable loss in X-Lost-Elements-Count.
Plain EN 16931 UBL → Peppol BIS UBL Fail-closed (CONVERSION_LOSSY_FAILCLOSED) Peppol endpoints cannot be synthesised; use POST /v1/generate with an explicit peppol block on each party instead.
Hybrid PDF in + ZUGFeRD/Factur-X target Extract embedded CII + convert + re-wrap the PDF The source PDF comes back as you sent it when its embedded XML already carries the target profile’s identifier and resolves to the target format (see Notes); only the flags of its annotations are set as PDF/A asks. The embedded XML is validated; the PDF is not checked against PDF/A-3. In every other case the engine re-wraps the PDF around the converted XML. With sourceFormat=auto a Factur-X or ZUGFeRD profile resolves as facturx, so a ZUGFeRD PDF sent to a zugferd target comes back as sent only with sourceFormat=zugferd, and is re-wrapped otherwise. A hybrid whose XML carries the plain EN 16931 identifier resolves as cii, not as zugferd or facturx, so it is always re-wrapped, even at targetProfile=en16931.
Hybrid PDF in + non-PDF target Extract embedded CII + convert + drop envelope Envelope drop is recorded as acceptable loss.
XML in + ZUGFeRD/Factur-X target URN swap only: returns CII XML, not a PDF Beliq does not wrap a standalone XML invoice into a hybrid PDF. To produce a ZUGFeRD/Factur-X hybrid PDF, call POST /v1/generate with output: "pdf" from structured JSON.
FatturaPA / Facturae / KSeF / SDI messaggio source or target Fail-closed (CONVERSION_UNSUPPORTED_PAIR) Non-EN-16931 national formats, with no published cross-format converter.

A PDF sent for a ZUGFeRD or Factur-X target

When the embedded XML of a hybrid PDF is not already the target, the engine converts that XML like any other source and validates the result. A result that fails answers 422 CONVERSION_OUTPUT_INVALID, and no PDF is built. A valid result goes into the PDF you sent, in four steps:

  1. The old invoice comes out of the embedded files. In the document’s list of embedded files (the catalog’s /AF array and its EmbeddedFiles name tree), every file under a name Beliq reads as an invoice (factur-x.xml, zugferd-invoice.xml, ZUGFeRD-invoice.xml or xrechnung.xml, in any letter case) is taken out, and so is the file the invoice was read from. Other embedded files stay, and so does a file attached to a page through a file attachment annotation, whatever its name. An invoice file taken out that is not the one converted is listed in X-Lost-Elements as a warning, at the path /Pdf/EmbeddedFiles.
  2. The PDF is prepared as a PDF/A-3b base, as for a PDF that POST /v1/generate builds: its XMP metadata is replaced with the PDF/A-3b markers, an sRGB output intent is declared, and the flags of its annotations are set as PDF/A asks. The content of its pages is not changed.
  3. Mustang embeds the converted XML as the one invoice file of that list: factur-x.xml for a facturx target, and zugferd-invoice.xml with the XMP Version 2p0 for a zugferd target, so a ZUGFeRD PDF whose invoice file was factur-x.xml comes back with zugferd-invoice.xml. These are the names Mustang writes; no FeRD document Beliq has read settles the ZUGFeRD one. The XMP names the converted XML’s profile as its conformance level. The engine reads the result back: its list of embedded files must hold the converted XML, byte for byte, as its one invoice file.
  4. veraPDF 1.30.3 checks the file against its PDF/A-3b profile. The PDF is returned only when veraPDF reports no violation.

With a test key the PDF is watermarked after these checks, and the watermarked file does not pass veraPDF’s PDF/A-3b profile. With a live key it comes back as veraPDF checked it.

A PDF that cannot be re-wrapped is not returned:

Answer When
422 CONVERSION_UNSUPPORTED_PAIR with error.details.sourceFormat pdf-not-rewrappable The engine cannot open the PDF you sent within its limits, Mustang refuses it, or veraPDF reports a violation in the re-wrapped file or cannot read it. A PDF that would come back as you sent it gets the same answer when the engine cannot set its annotation flags within its limits. After a violation, error.details.failedRules lists the rules, in the shape PDFA_VIOLATION uses. error.message says which it was. The quota unit is kept, as on every 422 of this route
422 CONVERSION_UNSUPPORTED_PAIR with error.details.reason pdf-rewrap-not-offered The converted XML’s profile is one Beliq builds no hybrid PDF for, such as Factur-X EXTENDED-CTC-FR, or the XML is larger than the engine reads from a PDF
500 CONVERSION_TOOL_ERROR A tool of the re-wrap failed: the PDF could not be prepared or read back, Mustang or veraPDF crashed or timed out, veraPDF’s report could not be read, or the PDF Mustang wrote does not hold the converted XML as its one invoice file. error.details.tool names the tool. The quota unit is refunded
503 CONVERSION_TOOL_UNAVAILABLE Mustang or veraPDF is not deployed on the engine. The quota unit is refunded
Response

Response

Success (200)

The body is raw bytes: application/xml or application/pdf depending on the envelope. The audit metadata is in the response headers:

Response header Description
Content-Type application/xml or application/pdf.
X-Source-Format Resolved source format (the engine’s auto-detect result if sourceFormat=auto was passed).
X-Target-Format Echo of the requested target format.
X-Profile-Detected Profile label inferred from the source’s CustomizationID / guideline URN (e.g. en16931, xrechnung, peppol-bis, facturx:basic). Absent when the source’s identifier is not one the engine names.
X-Lost-Elements-Count Number of acceptable-loss entries this conversion recorded (Peppol endpoint strips, dropped PDF envelopes, France CTC opt-in losses, invoice files a PDF re-wrap took out). Equals JSON.parse(X-Lost-Elements).length, unless the list passed its cap and was replaced by the truncated entry (see X-Lost-Elements).
X-Lost-Elements JSON-encoded array of LostElement entries: the losses the engine recorded. Always present ("[]" when it recorded none, which does not prove that nothing was lost; see the top of this page). Defensive 6KB cap: if the serialised list would exceed the cap, the header carries a single {"truncated": true, …} sentinel and X-Lost-Elements-Count remains the exact count. The cap should never fire in practice.
X-Conversion-Tools Comma-separated engine-internal labels in execution order (e.g. pdf-extract,mustang,urn-swap, helger-ubl2cii,urn-swap). Useful for audit; don’t switch caller logic on these.
X-Output-Envelope xml or pdf, matching Content-Type.
X-Version-Block Compact JSON object naming the artifact versions this engine build pins for the target format: schematronVersion (left out for a zugferd or facturx target at targetProfile minimum, basicwl, basic or extended, whose own Factur-X pack takes the EN 16931 pack’s place), plus ciusVersion, peppolVersion, or facturxVersion and facturxProfile for the targets that have them. Omitted when empty. It names pins and is not the verdict on the converted document. The fields are named as in the POST /v1/validate response body; Beliq-Ruleset selection fields are absent here because convert takes no ruleset selection: the output is always validated against the latest ruleset.

LostElement schema

Each entry in X-Lost-Elements has this shape:

Field Type Description
path string XPath of the lost element in the source document; /Pdf/EmbeddedFiles for an invoice file a PDF re-wrap took out.
value string | null Element text content where applicable; the file name for an invoice file a PDF re-wrap took out, or null when that name is over 255 UTF-16 code units or holds U+0000 or half of a surrogate pair; null for envelope-level losses (e.g. dropped PDF envelope).
reason string Human-readable explanation of why the loss happened.
severity "info" | "warning" "warning" for an invoice file a PDF re-wrap took out, "info" for every other entry.
acceptableLoss boolean Always true on a 200 response. The two losses the engine treats as unacceptable surface as 422 CONVERSION_LOSSY_FAILCLOSED instead, and losses it does not detect are not listed at all.

Worked example: Peppol BIS UBL → plain EN 16931 UBL

Converting a Peppol invoice carrying both endpoint blocks to plain EN 16931 strips both as an acceptable loss:

[
  {
    "path": "/Invoice/{urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2}AccountingSupplierParty/{urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2}Party/cbc:EndpointID",
    "value": null,
    "reason": "Peppol endpoint stripped (target is not peppol-bis)",
    "severity": "info",
    "acceptableLoss": true
  },
  {
    "path": "/Invoice/{urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2}AccountingCustomerParty/{urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2}Party/cbc:EndpointID",
    "value": null,
    "reason": "Peppol endpoint stripped (target is not peppol-bis)",
    "severity": "info",
    "acceptableLoss": true
  }
]

The corresponding response carries X-Lost-Elements-Count: 2.

Lossy conversions: semantics

Beliq detects two kinds of loss, with very different behaviour, and does not detect the rest:

  • Fail-closed losses (default): the engine detects a field the target cannot represent and the caller has not opted in. Returns 422 CONVERSION_LOSSY_FAILCLOSED with the un-mappable XPath(s) in error.details.unmappablePaths. No body is returned. Today this fires on two paths: a French cadre de facturation code (BT-23) on a UBL-syntax target (opt-in: ?dropFranceCtcOverlay=true), and synthesising a Peppol BIS endpoint pair on a non-Peppol source (no opt-in; use /v1/generate instead).

  • Acceptable losses: the loss is intrinsic to the requested conversion and the caller is implicitly asking for it. Returns 200 OK with the count surfaced in X-Lost-Elements-Count. Today this fires on: stripping Peppol cbc:EndpointID blocks when leaving peppol-bis, dropping the PDF envelope when converting a hybrid PDF to a target with no PDF spec, dropping a French cadre de facturation code when dropFranceCtcOverlay=true was set, and taking an invoice file other than the converted one out of a PDF that is re-wrapped.

  • Undetected losses: everything else. Apart from writing back the invoice total VAT amount (BT-110) that the UBL to CII converter leaves out (see the top of this page), the engine does not compare the output with the source, so a field a converter does not carry across is not listed in X-Lost-Elements. Where the loss breaks a rule of the target format, validating the output refuses the conversion with 422 CONVERSION_OUTPUT_INVALID. Where no rule sees it, the document comes back as 200 OK.

Error responses

Error responses

HTTP Status Error Code When
400 VALIDATION_ERROR Request body is empty or query parameters are invalid
422 CONVERSION_UNSUPPORTED_PAIR Source format is outside the EN 16931 family (FatturaPA / Facturae / KSeF / SDI messaggio source or target), the source body is a PDF with no embedded invoice XML, a PDF source that cannot be re-wrapped for a ZUGFeRD / Factur-X target (error.details.sourceFormat is pdf-not-rewrappable), or the request is one of the three refusals that carry a reason: a source with no specification identifier, an identifier rewrite that is not offered for the source’s profile, or a converted XML Beliq builds no hybrid PDF for
422 CONVERSION_OUTPUT_INVALID The converted document failed validation and is not returned. The verdict is in error.details.validationResult; see the details
422 CONVERSION_LOSSY_FAILCLOSED Source carries fields the target cannot represent; see the lossy-conversion semantics above
500 CONVERSION_TOOL_ERROR A converter (Mustang cii2ubl, Helger ubl2cii) failed or returned a document that could not be used, the validation of the converted document could not finish, or a tool of a PDF re-wrap failed. The response is not a verdict on the document
503 CONVERSION_TOOL_UNAVAILABLE A required JAR is not deployed on the engine (operational; should not happen in production)
413 VALIDATION_ERROR Request body is larger than this endpoint’s 1 MiB size limit. The cap is enforced before the body is read, so nothing was validated and no quota unit is spent. A body within the limit that the engine refuses as too large to process is also a 413, and its message says so: that body was read, and a live key’s quota unit is refunded
415 VALIDATION_ERROR The Content-Type is not one this route reads. Send application/xml, text/xml, application/octet-stream or application/pdf. Two other types get no 415: a JSON object or array sent as application/json gets a 400, and text/plain is read as UTF-8 text, which works for a UTF-8 XML document but not for a PDF. The free public demo is narrower: XML only, and PDF bytes are refused even when labelled as XML
503 ENGINE_UNAVAILABLE The engine is temporarily unreachable (generic 5xx path)
503 DATABASE_UNAVAILABLE Beliq’s database could not serve the request. Retry after Retry-After. Quota refunds says whether a spent unit comes back

Both CONVERSION_TOOL_* codes and ENGINE_UNAVAILABLE are failures on our side: they refund the document’s quota unit and confirm it on the x-beliq-quota-refunded response header. See Quota refunds.

A 422 keeps its unit, and that includes CONVERSION_OUTPUT_INVALID. The code does not say why the converted document fails: the content may not fit the target, the source may have been invalid already, or a converter may have changed it. Beliq validates the output and not the source, so it cannot tell the three apart. Send the source to POST /v1/validate to see whether it was valid before the conversion.

422 CONVERSION_UNSUPPORTED_PAIR example

{
  "success": false,
  "error": {
    "code": "CONVERSION_UNSUPPORTED_PAIR",
    "message": "Source format 'facturae' is not in the EN 16931 family (supported: cii / ubl / zugferd / facturx / xrechnung / peppol-bis).",
    "details": {
      "sourceFormat": "facturae",
      "targetFormat": "ubl"
    }
  }
}

Refusals that carry a reason

Three refusals answer 422 CONVERSION_UNSUPPORTED_PAIR although both formats are in the EN 16931 family. Each names itself in error.details.reason. A CONVERSION_UNSUPPORTED_PAIR for any other cause carries no reason.

reason When
missing-specification-identifier The source states no specification identifier (BT-24): cbc:CustomizationID in UBL, ram:GuidelineSpecifiedDocumentContextParameter/ram:ID in CII. Absent, empty and white space all count. Nothing is converted, whatever the target
profile-relabel-not-offered The request keeps the syntax (CII to CII, or UBL to UBL) and would write another identifier over the source’s. Refused when the source’s identifier is not one the engine names, and when the target carries the plain EN 16931 identifier while the source’s profile extends EN 16931 (Factur-X EXTENDED or EXTENDED-CTC-FR)
pdf-rewrap-not-offered The source is a PDF, the target is zugferd or facturx, and the converted XML’s profile is one Beliq builds no hybrid PDF for, such as Factur-X EXTENDED-CTC-FR, or the XML is larger than the engine reads from a PDF. Convert to an XML target instead

A Factur-X EXTENDED document sent with targetFormat=cii:

{
  "success": false,
  "error": {
    "code": "CONVERSION_UNSUPPORTED_PAIR",
    "message": "The source's profile (facturx:extended) extends EN 16931, and the target identifier 'urn:cen.eu:en16931:2017' claims the core model alone. Rewriting the identifier would leave content the target does not define under it.",
    "details": {
      "sourceFormat": "facturx",
      "targetFormat": "cii",
      "reason": "profile-relabel-not-offered"
    }
  }
}

422 CONVERSION_OUTPUT_INVALID details

The conversion ran, the result was validated, and it did not pass. No document is returned, the quota unit is kept and no webhook is sent.

Field Type Description
details.sourceFormat string The format the source resolved to.
details.targetFormat string The requested target format.
details.validationResult object The verdict on the converted document, with the fields the validation result object describes. valid is false and errors holds at least one finding. A finding’s location points into the converted document, not into the source you sent. There is no sha256: /v1/validate computes that one over its request body.

422 CONVERSION_LOSSY_FAILCLOSED example (France CTC)

{
  "success": false,
  "error": {
    "code": "CONVERSION_LOSSY_FAILCLOSED",
    "message": "Source CII carries French cadre de facturation code 'B8' (BT-23); UBL has no equivalent extension and Mustang cii2ubl would silently drop it. Pass options.dropFranceCtcOverlay=true (or the ?drop_france_ctc_overlay=true query param) to accept the loss explicitly.",
    "details": {
      "unmappablePaths": [
        "/rsm:CrossIndustryInvoice/rsm:ExchangedDocumentContext/ram:BusinessProcessSpecifiedDocumentContextParameter/ram:ID"
      ]
    }
  }
}

503 CONVERSION_TOOL_UNAVAILABLE example

{
  "success": false,
  "error": {
    "code": "CONVERSION_TOOL_UNAVAILABLE",
    "message": "Mustang JAR not available",
    "details": {
      "missingTool": "mustang"
    }
  }
}
Notes

Notes

  • Lost-elements audit lives in headers (not in the response body) so callers using --output file curl idioms get the converted bytes verbatim while still being able to inspect the audit trail. Browsers must read these via Access-Control-Expose-Headers (already configured on beliq-api).
  • The engine resolves sourceFormat=auto by inspecting the XML root + the CustomizationID / GuidelineSpecifiedDocumentContextParameter/ID URN. A document that carries the plain EN 16931 URN resolves as cii or ubl, and so does one whose URN the engine does not name. A Factur-X or ZUGFeRD EN 16931 document resolves the same way, because that profile carries the plain urn:cen.eu:en16931:2017 URN. A source with no URN at all is refused (reason missing-specification-identifier).
  • For ZUGFeRD vs Factur-X disambiguation (both share the same CII URN family), the engine treats them as profile labels on a CII document. Source-format resolution returns facturx by default; pass sourceFormat=zugferd explicitly when the caller’s intent is ZUGFeRD-specific.
  • See Cross-format conversion on the format overview page for the conceptual map.