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
facturxorzugferdtarget attargetProfile=en16931the 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
cii2ublruns as published. Helgerubl2ciileaves 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, andX-Conversion-Toolsnamesbt110-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 answers422 CONVERSION_OUTPUT_INVALID: an EN 16931 invoice with lines converted tominimumorbasicwl, two profiles that hold no invoice lines; a MINIMUM or BASIC WL document converted toen16931, which does not gain the lines it lacks; a plain invoice converted toxrechnungthat 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
ciiandubl, andfacturxorzugferdaten16931) 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 answers422 CONVERSION_UNSUPPORTED_PAIRwith areasoninerror.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.targetProfilecounts: a Factur-X BASIC document sent withtargetFormat=facturx&targetProfile=minimumis not returned as BASIC. Its identifier is rewritten to MINIMUM and the result is validated as MINIMUM. An XRechnung CII document sent withtargetFormat=xrechnungis 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
Convert CII → UBL:
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.xmlConvert a ZUGFeRD PDF → UBL XML (extracts the embedded CII; the PDF envelope is dropped and recorded as an acceptable loss):
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.xmlConvert a Peppol BIS UBL → plain EN 16931 UBL (strips the cbc:EndpointID blocks; acceptable loss recorded):
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
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
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:
- The old invoice comes out of the embedded files. In the document’s list of embedded files (the catalog’s
/AFarray and itsEmbeddedFilesname tree), every file under a name Beliq reads as an invoice (factur-x.xml,zugferd-invoice.xml,ZUGFeRD-invoice.xmlorxrechnung.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 inX-Lost-Elementsas a warning, at the path/Pdf/EmbeddedFiles. - The PDF is prepared as a PDF/A-3b base, as for a PDF that
POST /v1/generatebuilds: 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. - Mustang embeds the converted XML as the one invoice file of that list:
factur-x.xmlfor afacturxtarget, andzugferd-invoice.xmlwith the XMPVersion2p0for azugferdtarget, so a ZUGFeRD PDF whose invoice file wasfactur-x.xmlcomes back withzugferd-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. - 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
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_FAILCLOSEDwith the un-mappable XPath(s) inerror.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/generateinstead). -
Acceptable losses: the loss is intrinsic to the requested conversion and the caller is implicitly asking for it. Returns
200 OKwith the count surfaced inX-Lost-Elements-Count. Today this fires on: stripping Peppolcbc:EndpointIDblocks when leavingpeppol-bis, dropping the PDF envelope when converting a hybrid PDF to a target with no PDF spec, dropping a French cadre de facturation code whendropFranceCtcOverlay=truewas 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 with422 CONVERSION_OUTPUT_INVALID. Where no rule sees it, the document comes back as200 OK.
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
- Lost-elements audit lives in headers (not in the response body) so callers using
--output filecurl idioms get the converted bytes verbatim while still being able to inspect the audit trail. Browsers must read these viaAccess-Control-Expose-Headers(already configured onbeliq-api). - The engine resolves
sourceFormat=autoby inspecting the XML root + theCustomizationID/GuidelineSpecifiedDocumentContextParameter/IDURN. A document that carries the plain EN 16931 URN resolves asciiorubl, 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 plainurn:cen.eu:en16931:2017URN. A source with no URN at all is refused (reasonmissing-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
facturxby default; passsourceFormat=zugferdexplicitly when the caller’s intent is ZUGFeRD-specific. - See Cross-format conversion on the format overview page for the conceptual map.