Error reference
Complete reference of all Beliq API error codes, HTTP statuses, and remediation guidance.
All API errors use the same response shape:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of the error",
"details": {}
}
}| Field | Type | Description |
|---|---|---|
code |
string | Stable, machine-readable error code |
message |
string | Human-readable explanation |
details |
object? | Structured data for the codes that carry it: fields on a schema VALIDATION_ERROR, validationResult on INVALID_INVOICE, allowedProfiles on PROFILE_STANDARD_MISMATCH, creditNoteCapableStandards or typeCodeCapableStandards on DOCUMENT_TYPE_STANDARD_MISMATCH, unmappablePaths on CONVERSION_LOSSY_FAILCLOSED, validationResult on CONVERSION_OUTPUT_INVALID, failedRules on PDFA_VIOLATION, sourceFormat, targetFormat, for three refusals a reason and, after a veraPDF violation in a re-wrapped PDF, failedRules on CONVERSION_UNSUPPORTED_PAIR, tool and exitCode on CONVERSION_TOOL_ERROR, missingTool on CONVERSION_TOOL_UNAVAILABLE. Absent on every other code |
incidentId |
string? | Support correlation ID on a 5xx that an endpoint in this reference raises itself; never on a 4xx (see Request ID and incident ID). Quote it together with the x-request-id header when you email hello@beliq.eu |
That includes a request the router refuses before it reaches an endpoint: a URL that does not decode, or a path parameter longer than the router accepts, gets a 400 VALIDATION_ERROR.
Error codes
| Code | HTTP Status | Meaning | Endpoints | Remediation |
|---|---|---|---|---|
VALIDATION_ERROR |
400 / 413 / 415 / 422 | Request body is invalid. This code is also the catch-all for client errors that have no more specific code, so read the status to tell them apart: 400 the body failed the schema, the engine refused a value the schema allows, or the URL could not be routed, 413 it exceeded the size limit and was never read, or the engine refused a body within that limit as too large to process, 415 the content type is not accepted on that route, 422 the engine judged the document unprocessable | All | For a 400 with details.fields, check required fields and types in the generate schema. A 400 without details names its cause in message; on /v1/generate that can be the engine refusing a value the schema allows, and the message then says what it refused. For the others the status is the diagnosis; the message names the specific cause |
INVALID_INVOICE |
422 | Invoice data is structurally valid JSON but fails validation: EN 16931 or CIUS Schematron for UBL/CII, or authority XSD for the national formats (FatturaPA, Facturae, e-SLOG, KSeF FA(3)) | /v1/generate |
Inspect details.validationResult.errors (and for the national formats, structural XSD findings where applicable) |
PROFILE_STANDARD_MISMATCH |
422 | The chosen profile is not allowed for the chosen standard |
/v1/generate |
Use a compatible combination; the response details includes standard, profile, and allowedProfiles. See the profile compatibility table. |
DOCUMENT_TYPE_STANDARD_MISMATCH |
422 | documentType: "creditnote" with a standard that does not accept CreditNote payloads, today fatturapa, facturae and ksef (invoices only), or a typeCode with a standard that writes its own document type (fatturapa, facturae, eslog, ksef). |
/v1/generate |
For a credit note, inspect details.creditNoteCapableStandards; it lists standards that honour CreditNotes in the deployed engine (facturx, peppol-bis, xrechnung, zugferd, eslog). See the Peppol Credit notes section. For a typeCode, inspect details.typeCodeCapableStandards, or omit the field to get the standard’s own default. |
PARSE_FAILED |
400 | Input file could not be parsed as a valid e-invoice | /v1/parse |
Verify the input is a well-formed UBL 2.1 or CII XML, or a ZUGFeRD / Factur-X hybrid PDF with an embedded CII attachment. FatturaPA and SDI messaggio inputs are not handled by parse; use /v1/validate for those. |
AUTHENTICATION_REQUIRED |
401 | No API key was sent | Every endpoint that takes an API key | Include Authorization: Bearer <key> or X-API-Key: <key> in the request headers |
INVALID_API_KEY |
403 | The key that was sent is not an active key: it is malformed, does not exist or has been revoked | Every endpoint that takes an API key | Verify the API key in the dashboard. Generate a new key if it was revoked |
QUOTA_EXCEEDED |
429 | Monthly usage quota exhausted: your plan’s quota for a live key, the sandbox allowance for a test key | Every endpoint that spends a unit: /v1/generate, /v1/validate, /v1/parse, /v1/convert, and /v1/send and /v1/receive (preview), which answer TRANSMISSION_DISABLED first while transmission is not enabled |
For a live key, upgrade your subscription plan in the dashboard, or wait until the window rolls over (Retry-After is the seconds until then; /v1/me reports the same instant as resetsAt). For a test key, no plan upgrade lifts the sandbox allowance: it resets at the start of the next UTC calendar month |
RATE_LIMITED |
429 | Too many requests in the current one-minute burst window. The transmission endpoints (preview) have limits of their own | All | Respect Retry-After and retry with backoff. Retry-After here is seconds, not minutes |
ACCOUNT_THROTTLED |
429 | The account is temporarily throttled after sustained pressure on its own limits. Unlike RATE_LIMITED, this blocks every /v1 route until it expires |
All | Respect Retry-After, which can be several minutes, much longer than a burst window. Stop retrying in a tight loop: further requests while throttled extend it. Reaching this state means requests were being sent well beyond the plan’s allowance for a sustained period |
ENGINE_UNAVAILABLE |
503 | Beliq could not process the request: the engine was unavailable, the request queued for too long, or too many of your account’s requests were already waiting (see Burst and sustained throughput) | /v1/generate, /v1/validate, /v1/parse, /v1/convert |
Retry after Retry-After. The document’s quota unit is refunded (see Quota refunds), so a retry does not cost you a second document. Safe to retry automatically. If persistent, check the status page and email hello@beliq.eu with the error.incidentId value |
DATABASE_UNAVAILABLE |
503 | Beliq’s database could not serve the request: it did not answer in time, a lock wait or a statement took too long, it had too many connections, or it had no primary or lost the connection for longer than Beliq retries | Every endpoint that takes an API key, /v1/me included. Not /v1/rulesets, which reads no database |
Retry after Retry-After. On /v1/generate, /v1/validate, /v1/parse, /v1/convert and /v1/me a retry is safe, and the Node and Python SDKs retry a 503 for you. A write the request started can still complete after the 503, so on /v1/send retry with the same Idempotency-Key. If the request had spent a quota unit, Beliq tries to refund it at once and says on x-beliq-quota-refunded whether that worked (see Quota refunds). If persistent, check the status page and email hello@beliq.eu with the error.incidentId value |
INTERNAL_ERROR |
500 | Unexpected server error | All | Retry the request. If persistent, email hello@beliq.eu with the error.incidentId value and the x-request-id response header |
NOT_FOUND |
404 | No route exists at the requested path and method, or the resource the request names does not exist | All | Check the path and HTTP method against this reference. A POST route called with GET returns this too; for an authenticated caller, or on a public route such as /v1/rulesets, it carries an Allow header listing the methods the path accepts |
PDF_TEMPLATE_AUTH_REQUIRED |
400 | pdfTemplateId was sent without an authenticated organization. Stored PDF templates are org-scoped, so they require an API key (the free, unauthenticated generator can use template: "standard" instead). |
/v1/generate |
Send the request with your Authorization: Bearer <key>, or drop pdfTemplateId and use template: "standard" for the built-in layout |
PDF_TEMPLATE_NOT_FOUND |
404 | The referenced pdfTemplateId does not exist in your organization |
/v1/generate |
Check the template’s ref in the dashboard under PDF templates |
PDF_TEMPLATE_INVALID |
422 | The stored template definition could not be parsed, or fails the checks the dashboard designer applies on save. The message names the field at fault | /v1/generate |
Open the template in the dashboard designer, fix the field it names and save; if it persists, email hello@beliq.eu with the x-request-id |
PDFA_VIOLATION |
422 | A hybrid PDF was built, veraPDF reported a violation of its PDF/A-3b profile in it, and so the PDF is not returned. The quota unit is refunded (see Quota refunds) | /v1/generate |
The same request fails the same way again, so do not retry it unchanged. Read details.failedRules. With your own template (pdfTemplateId), change the template in the dashboard designer. Without one, nothing in the PDF is yours to change: email hello@beliq.eu with the x-request-id response header |
CONVERSION_UNSUPPORTED_PAIR |
422 | The conversion is not offered: the source format is outside the EN 16931 family, the source PDF carries no embedded invoice XML, the source states no specification identifier, the identifier rewrite is not offered for the source’s profile, a PDF source cannot be re-wrapped for a ZUGFeRD or Factur-X target, or Beliq builds no hybrid PDF for the converted XML’s profile | /v1/convert |
Check the supported pairs, details.reason and, for a PDF source, the re-wrap. National formats (FatturaPA, Facturae, KSeF, SDI messaggio) have no cross-format converter |
CONVERSION_OUTPUT_INVALID |
422 | The converted document failed validation, so it is not returned. The quota unit is kept | /v1/convert |
Inspect details.validationResult.errors; the findings are on the converted document. Send the source to POST /v1/validate to see whether it was valid before the conversion, and see what a conversion does with the content |
CONVERSION_LOSSY_FAILCLOSED |
422 | The source carries fields the target cannot represent, so the conversion was refused rather than performed silently. No body is returned | /v1/convert |
Read details.unmappablePaths. For a France CTC Flux 2 code on a UBL target, opt in with ?dropFranceCtcOverlay=true; for a synthesised Peppol endpoint pair there is no opt-in, so use POST /v1/generate. See lossy conversions |
CONVERSION_TOOL_UNAVAILABLE |
503 | A converter the engine needs for this pair is not deployed. Operational, and not expected in production | /v1/convert |
Retry after Retry-After. The quota unit is refunded (see Quota refunds). If it persists, email hello@beliq.eu with the error.incidentId |
CONVERSION_TOOL_ERROR |
500 | A converter failed on this document or returned a document that could not be used, or the validation of the converted document could not finish. It is not a verdict on the document | /v1/convert |
The message names the cause. A converter fails the same way on the same bytes. The quota unit is refunded. Email hello@beliq.eu with the error.incidentId |
Transmission errors (preview)
| Code | HTTP Status | Meaning | Remediation |
|---|---|---|---|
TRANSMISSION_DISABLED |
503 | Transmission is not enabled on this deployment, so every transmission endpoint refuses the request. | Build and validate the file and deliver it through your own channel. Subscribe for the launch date. |
TRANSMISSION_NO_PROVIDER |
503 | Transmission is enabled, but no provider is currently available for the target network in your key’s mode, so the document would queue with no path forward. | If the response carries Retry-After, the provider failed recently and Beliq is checking whether it is back. After that many seconds a retry can be accepted as the first document of that check. It can also be refused again, with a new Retry-After, while the check is still running or after it failed. Without Retry-After, the provider is unavailable until Beliq changes something on its side, so an automatic retry meets the same refusal. The one exception is the sandbox test invoice (POST /v1/participants/sandbox/test-invoice), which never sends Retry-After with this code, so its refusal can also be one that a later retry gets past. If it persists, check the status page and email hello@beliq.eu with the x-request-id response header. |
IDEMPOTENCY_KEY_REUSED |
409 | An Idempotency-Key was reused with a different request body. |
Use a fresh key for a different document, or resend the identical body to get the existing transmission back. |
INVALID_IDEMPOTENCY_KEY |
400 | The Idempotency-Key header is longer than 255 characters or contains something other than printable ASCII. |
Shorten the key and keep it to printable ASCII. A UUID is the usual choice. Rejected before any quota is spent. |
SENDER_NOT_REGISTERED |
403 | The sender identifier is not a registered, active participant of your organization, so you may not send as it. |
Register the identifier and complete the ownership check before sending as it. The message says whether there is no registration at all or one that is not active yet. |
CONTENT_ALREADY_SENT |
409 | This exact document was already sent to this recipient and that transmission can still arrive, so nothing was queued. details.transmissionId names it. |
Poll the existing transmission. To make retries unambiguous, send an Idempotency-Key. A resend is accepted once the earlier transmission reaches failed, or rejected on a network where a rejection does not burn the invoice number (see FRANCE_INVOICE_NUMBER_BURNED). |
SENDER_COUNTRY_MISSING |
409 | The participant registration you are sending as records no country. Every Peppol envelope carries the sending entity’s country, so the registration cannot send until it is set. | Email hello@beliq.eu to complete the registration. |
RECIPIENT_NOT_ROUTABLE |
400 | The recipient identifier is not in a form the Peppol Network can address. |
A Peppol identifier is a four-digit scheme code and a value, for example 0088:5790000435975. |
DOCUMENT_PARTY_MISMATCH |
400 | The document’s own identifier for the sender or the recipient names a different party than the request does. On Peppol that is the electronic address; on Italy SdI it is the VAT number of each party the platform could send the document as. details says which role and gives both identifiers, and the message names the element that disagrees. |
Make the document and the request agree. On Peppol an EndpointID the invoice carries must be the participant you are sending as, and the one you are sending to; a document with none passes this check. POST /v1/send validates the document it builds, and POST /v1/transmissions validates nothing outside a France lane: it queues what you give it. On SdI it is CedentePrestatore/DatiAnagrafici/IdFiscaleIVA, and more: the transmission partner’s documentation says a self-invoice is sent as its buyer, without saying which documents count as one. So the buyer’s CessionarioCommittente/DatiAnagrafici/IdFiscaleIVA must name the sender too when any TipoDocumento is other than TD01 to TD06, TD24 or TD25, or when SoggettoEmittente is CC; with SoggettoEmittente TZ, the VAT in TerzoIntermediarioOSoggettoEmittente must. A document whose parties Beliq cannot read at all is refused the same way, with details.inDocument null: no XML parser runs on it, so any shape two conformant parsers could read differently is refused rather than guessed, and so is a document holding a NUL character or an element name outside ASCII. |
FRENCH_DOMESTIC_FLOW |
409 | Sender and recipient are both French, so this is a regulated domestic flow under the French B2B reform and must reach the network through a plateforme agréée rather than over Peppol. details gives both canonical identifiers. Not a document defect: the invoice may be perfectly valid. |
Send it through a plateforme agréée of your own. The partner PA lane of managed delivery is in build, so there is no French lane to enable yet. A French sender can still reach a non-French recipient over Peppol. |
FRANCE_CTC_BLOCKING_FINDINGS |
422 | The document trips one or more BR-FR-CTC Flux 2 rules. A plateforme agréée refuses a document carrying any of them whatever severity the rule pack assigns, so Beliq refuses before the send spends the invoice number. details.ruleIds lists them. Note that valid on POST /v1/validate reports the severity the rule pack assigns: the Flux 2 pack in force flags every one of these rules fatal, and an earlier version flagged most of them as warnings, so a validation that ran the earlier version can report valid: true for the same document. franceCtcBlockingRuleIds on the validation result is the field that answers for transmission. |
Correct what the listed rules point at and send again. Validate first with POST /v1/validate and check that franceCtcApplied is true and franceCtcBlockingRuleIds is absent. |
FRANCE_CTC_NOT_JUDGED |
422 | The document was not judged against the French CTC rules, so Beliq holds no verdict it can stand behind for a plateforme agréée. Almost always this is a missing BT-23 cadre de facturation, which is also what keeps BR-FR-08, the rule that would have reported it, from running. On POST /v1/transmissions there is a second cause: the BR-FR-CTC Flux 2 pack ran and gave no verdict, because its run failed, timed out or produced a report over the size limit. (On POST /v1/send such a run fails the composed document’s validation first, as INVALID_INVOICE.) |
Add BT-23 from the closed cadre list, or declare the EXTENDED-CTC-FR profile in BT-24. See France. If BT-23 already carries a code from that list, the pack did not finish. Nothing was queued: a failed or timed-out run can pass when you send again, while a report over the size limit repeats for the same document. Email hello@beliq.eu if it repeats. |
FRANCE_INVOICE_NUMBER_BURNED |
409 | A plateforme agréée already rejected this exact document. A French invoice number is unique on (number, supplier SIREN, invoice year) and a rejected invoice must be reissued under a new number, so Beliq refuses the resend rather than queue a second document under a spent one. details.transmissionId names the rejected transmission. |
Issue a corrected invoice under a new number and send that. Do not retry the same bytes: the platform’s own uniqueness check does not stop the reuse, which is why this one is on Beliq’s side. |
UNSUPPORTED_SYNTAX |
400 | Only UBL and CII documents can be sent over Peppol. | Convert the document first: see POST /v1/convert. |
MISSING_CUSTOMIZATION_ID |
400 | The document carries no CustomizationID, so its Peppol document type identifier cannot be derived and no receiving Access Point can be looked up. |
Add the CustomizationID for the profile you are sending, for example urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0. |
MISSING_PROCESS_ID |
400 | The document carries no ProfileID (UBL) or business process identifier (CII), so its Peppol process identifier cannot be derived. |
Add it, for example urn:fdc:peppol.eu:2017:poacc:billing:01:1.0. |
MALFORMED_DOCUMENT |
400 | The document is not well-formed XML, or it declares a DOCTYPE. | Fix the XML. A DOCTYPE is refused outright: no valid e-invoice declares one. |
EMPTY_DOCUMENT |
400 | The document body is empty. | Send the invoice XML in the request body. |
INSUFFICIENT_ROLE |
403 | The API key or member calling POST /v1/participants/sandbox or POST /v1/participants/sandbox/test-invoice does not hold a role that may write integration resources. |
Call it as an owner, admin or developer. A test identifier spends a registration in a partner account, which is why a viewer or a billing member cannot issue one. |
ENROLLMENT_MODE_UNSUPPORTED |
409 | The provider serving that network obtains a customer’s authorization in a way Beliq has no self-service flow for, so there is nothing a corrected request would reach. | Not a retry: waiting does not clear it. Email hello@beliq.eu to register on that network. |
ENROLLMENT_REFUSED |
422 | Beliq’s own checks passed and the delivery partner refused the registration anyway. | The partner’s own wording is deliberately not relayed: the refusal Beliq most expects, an identifier another account already holds, is indistinguishable on the wire from any other, so repeating it would read as a diagnosis Beliq cannot make. Retry once; if it persists, email hello@beliq.eu with the x-request-id response header. |
INBOX_UNKNOWN_PROVIDER |
404 | An inbound network callback referenced a provider that is not registered. | Provider-side authentication error, not reachable with an API key. |
INBOX_VERIFICATION_FAILED |
401 | An inbound network callback or document failed signature verification. | Provider-side authentication error, not reachable with an API key. |
INBOX_SIGNATURE_EXPIRED |
401 | An inbound network callback carried a valid signature whose timestamp fell outside the accepted freshness window, so it is refused as a replay. | Provider-side authentication error, not reachable with an API key. |
HTTP status code summary
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request: invalid JSON, missing required fields, or unsupported format combination |
| 401 | Unauthorized: no API key was sent |
| 403 | Forbidden: the key that was sent is malformed, does not exist or has been revoked, or you may not send as the sender identifier (transmission, preview) |
| 404 | Not found: no route exists at the requested path and method, or a referenced resource (e.g. a pdfTemplateId) does not exist in your organization |
| 409 | Conflict: an Idempotency-Key was reused with a different body, or the same document was already sent to that recipient (transmission, preview) |
| 413 | Payload too large: the request body exceeds the endpoint’s size limit and is not read, or the engine refused a body within that limit as too large to process |
| 415 | Unsupported media type: the Content-Type is not one this route reads. /v1/validate, /v1/parse and /v1/convert take application/xml, text/xml, application/octet-stream or application/pdf; on those three, a JSON object or array sent as application/json gets a 400 instead, and text/plain is read as UTF-8 text (each route’s 415 row says more); the free public demo is narrower still and takes XML only |
| 422 | Unprocessable entity: input is syntactically valid but fails business rules or parsing |
| 429 | Too many requests: quota exceeded or rate limited |
| 500 | Internal server error |
| 503 | Service unavailable: the engine or Beliq’s database is temporarily unavailable, or transmission is not enabled |
Request size limits
Each endpoint caps the size of the request body. The cap is enforced before the body is read, so an oversize request is refused without being parsed or validated, and it never spends a quota unit.
| Endpoint | Limit |
|---|---|
POST /v1/validate |
1 MiB |
POST /v1/parse |
1 MiB |
POST /v1/generate |
1 MiB |
POST /v1/convert |
1 MiB |
POST /v1/send (preview) |
1 MiB |
POST /v1/receive (preview) |
1 MiB |
POST /v1/transmissions (preview) |
5 MiB |
POST /v1/inbox/:providerId (preview) |
5 MiB |
Exceeding the limit returns 413 with code VALIDATION_ERROR:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request body exceeds the size limit of 1 MiB for this endpoint. The body was not read, so this is not a validation failure. Send a smaller document."
}
}A body within the limit can still be too large for the engine to process. On
/v1/generate, /v1/validate, /v1/parse and /v1/convert that is also a
413 with code VALIDATION_ERROR, whose message says the request is within
this endpoint’s body size limit but too large to process; the body was read, and
a live key’s quota unit is refunded (see the next section).
On /v1/send and /v1/receive (preview) the same refusal is a 400
VALIDATION_ERROR that keeps its unit.
The code is the coarse class tag shared by every 4xx an endpoint answers that is
not one of the specific codes above; the 413 status is the discriminator. Read
the status, not the code, to tell an oversize body from a schema failure.
Quota refunds on a Beliq-side failure
When a request fails for a reason on our side rather than in your document, the quota unit that request spent goes back. That covers every ENGINE_UNAVAILABLE (503), and the conversion-tool failures on /v1/convert (CONVERSION_TOOL_UNAVAILABLE, CONVERSION_TOOL_ERROR). It also covers a body the engine refused as too large on /v1/generate, /v1/validate, /v1/parse and /v1/convert (a 413, see Request size limits), and two answers of POST /v1/generate for a PDF that was not delivered: a 500 INTERNAL_ERROR when the PDF could not be produced, and a 422 PDFA_VIOLATION, which is about the PDF page and not about your invoice data. It does not cover any other 4xx that names something in your document: that is an answer you asked for, and it keeps its unit. A CONVERSION_OUTPUT_INVALID (422) on /v1/convert keeps its unit as well, whatever made the converted document fail: Beliq validates the output and not the source, so it cannot tell a source that was already invalid from one a converter changed. POST /v1/convert says more.
A DATABASE_UNAVAILABLE (503) is a failure on our side too. If the request had spent a unit, Beliq tries the refund at once. It does the same when the charge itself was cut off and may have gone through. While the database is down that refund usually cannot be applied. Most often the database fails the request before any unit is spent, because the key check comes first: then nothing was charged and there is nothing to refund.
Every response that refunds, or should have, says so on the x-beliq-quota-refunded header:
| Header | Meaning |
|---|---|
true |
The unit went back. A retry costs you nothing. |
false |
The unit was not returned: the cap below applied, or the refund could not be applied, which is the usual outcome on a DATABASE_UNAVAILABLE. |
| absent | No live unit was at stake (a successful call, a document error, a DATABASE_UNAVAILABLE that came before any unit was spent, or a test-key request, which meters against the sandbox allowance instead). |
Refunds are unconditional for the failures where nothing was computed: an unreachable engine, a shed request, a missing conversion tool, a body the engine refused as too large. Where a failure did consume engine compute (a timeout, a fault raised part-way through processing, or a PDF that was built and then not delivered) refunds are capped at 50 per organization per clock hour. The cap exists so a deliberately expensive document cannot be run repeatedly at no cost; normal traffic through a real incident stays well under it. A DATABASE_UNAVAILABLE refund is not capped. If you see x-beliq-quota-refunded: false on volume you consider legitimate, or on any DATABASE_UNAVAILABLE, email hello@beliq.eu with the error.incidentId, or with the x-request-id response header when the answer carries none, and we will reconcile it.
VALIDATION_ERROR details
A VALIDATION_ERROR raised by JSON schema validation carries details.fields, one entry per rejected constraint. This is the 400 on POST /v1/generate for a body that fails the schema, and on POST /v1/validate, /v1/parse and /v1/convert for a query string that fails it. A body that passes the schema can still be refused by the engine, for a rule the generate page names, such as a buyer taxId on Factur-X basic: that 400 carries no details, and its message says what was refused. The 400 for a URL the router cannot route carries no details either. The other statuses that share the code (413, 415, and any other 4xx with no more specific code) reject before or outside the schema and carry no details at all.
| Field | Type | Description |
|---|---|---|
path |
string? | Where the constraint failed. A JSON Pointer into the request body (/invoice/lines/0/unitPrice), or into the query string on the three query routes (/format), except for a property missing at the top level, where it is the bare property name (standard). An undeclared property is named by its own pointer, /invoice/foo, or /foo at the top level. Absent when the failure is at the root of the body and names no property, such as a body that is not a JSON object |
message |
string | The constraint in words, relative to path (must have required property 'number', must be number) |
keyword |
string | The JSON Schema keyword that rejected it (required, type, const, anyOf, minimum) |
A missing nested property reports the parent object as path and names the child in message, so { "standard": "xrechnung", "output": "xml", "invoice": { "issueDate": "2026-04-13" } } answers:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "body/invoice must have required property 'number'",
"details": {
"fields": [
{
"path": "/invoice",
"message": "must have required property 'number'",
"keyword": "required"
}
]
}
}
}One bad value can produce several entries. A standard outside the accepted set is checked against each allowed value in turn, so it yields one const entry per candidate plus a closing anyOf entry, all of them on the same path. Read fields as the list of constraints that failed, not as a list of distinct problems, and key your own error reporting on path.
INVALID_INVOICE details
INVALID_INVOICE includes a full validationResult in details, matching the validation result from /v1/validate:
{
"success": false,
"error": {
"code": "INVALID_INVOICE",
"message": "Invoice validation failed with 1 error",
"details": {
"validationResult": {
"valid": false,
"format": "cii",
"schematronVersion": "1.3.16",
"ciusVersion": "XRechnung-2.6.0",
"errors": [
{
"ruleId": "BR-DE-15",
"severity": "error",
"location": "/rsm:CrossIndustryInvoice",
"message": "An XRechnung invoice must contain a buyer reference (BT-10)."
}
],
"warnings": []
}
}
}
}PROFILE_STANDARD_MISMATCH details
PROFILE_STANDARD_MISMATCH includes the rejected combination so you can correct the request:
{
"success": false,
"error": {
"code": "PROFILE_STANDARD_MISMATCH",
"message": "Profile 'extended' is not allowed for standard 'peppol-bis'.",
"details": {
"standard": "peppol-bis",
"profile": "extended",
"allowedProfiles": ["peppol"]
}
}
}DOCUMENT_TYPE_STANDARD_MISMATCH details
DOCUMENT_TYPE_STANDARD_MISMATCH is returned when documentType: "creditnote" is sent for a standard that only supports invoices, notably fatturapa, facturae and ksef. The four EN 16931 hybrid/XML routes peppol-bis, facturx, xrechnung, and zugferd accept CreditNotes, as does the national eslog route (document type code 381 on the same e-SLOG message). Inspect creditNoteCapableStandards in error details; it mirrors the deployed engine capability set.
{
"success": false,
"error": {
"code": "DOCUMENT_TYPE_STANDARD_MISMATCH",
"message": "documentType='creditnote' is not supported for standard 'fatturapa'. Accepted for CreditNote payloads: ['eslog', 'facturx', 'peppol-bis', 'xrechnung', 'zugferd'].",
"details": {
"standard": "fatturapa",
"documentType": "creditnote",
"creditNoteCapableStandards": ["eslog", "facturx", "peppol-bis", "xrechnung", "zugferd"]
}
}
}The same code answers a typeCode (BT-3)
sent with a standard whose builder writes its own document type: fatturapa,
facturae, eslog and ksef. Beliq refuses rather than build the document with
the standard’s default code in place of yours, which would send a corrected invoice
out as a commercial one. Here details carries typeCode and
typeCodeCapableStandards instead of the credit note keys, so read whichever pair is
present.
{
"success": false,
"error": {
"code": "DOCUMENT_TYPE_STANDARD_MISMATCH",
"message": "typeCode='384' is not supported for standard 'fatturapa', whose builder writes its own document type. Accepted for typeCode: ['facturx', 'peppol-bis', 'xrechnung', 'zugferd'].",
"details": {
"standard": "fatturapa",
"typeCode": "384",
"typeCodeCapableStandards": ["facturx", "peppol-bis", "xrechnung", "zugferd"]
}
}
}PDFA_VIOLATION details
POST /v1/generate builds a ZUGFeRD or Factur-X PDF and then runs veraPDF 1.30.3 on the finished file (the PDF/A check on a hybrid PDF). When veraPDF reports a violation of its PDF/A-3b profile, the PDF is not returned and the answer is 422 PDFA_VIOLATION. details.failedRules lists the rules veraPDF reported as failed, at most 20 of them. Each entry holds these keys, under the names and with the values veraPDF’s own report gives them:
| Key | Type | Description |
|---|---|---|
specification |
string | The specification the rule belongs to, as veraPDF names it (ISO 19005-3:2012) |
clause |
string | The clause of that specification (6.2.11.4.1) |
testNumber |
integer | veraPDF’s number of the test inside the clause |
description |
string | veraPDF’s text for the rule |
failedChecks |
integer | How many checks of the rule veraPDF counted as failed in the file |
{
"success": false,
"error": {
"code": "PDFA_VIOLATION",
"message": "veraPDF reported a violation of its PDF/A-3b profile in the generated PDF, so the PDF was not returned.",
"details": {
"failedRules": [
{
"specification": "ISO 19005-3:2012",
"clause": "6.2.11.4.1",
"testNumber": 1,
"description": "The font programs for all fonts used for rendering within a conforming file shall be embedded within that file, as defined in ISO 32000-1:2008, 9.9",
"failedChecks": 3
}
]
}
}
}The list is veraPDF’s: Beliq adds no rule and rewrites no text. veraPDF is run with a limit on the failed checks it collects for one file, so failedChecks has an upper bound and the list may not hold every rule the file breaks. It keeps of each rule only what veraPDF’s report holds under these five names with these types, so an entry can lack a key and the list can be empty. The answer carries no incidentId.
PDF template errors
These are returned only when a pdfTemplateId is referenced on POST /v1/generate. Stored templates are designed in the dashboard and are scoped to your organization, so they require an authenticated request.
| Code | When | Fix |
|---|---|---|
PDF_TEMPLATE_AUTH_REQUIRED |
pdfTemplateId sent on an unauthenticated request |
Use your API key, or switch to template: "standard" |
PDF_TEMPLATE_NOT_FOUND |
The ref is not a template in your organization | Verify the ref in the dashboard |
PDF_TEMPLATE_INVALID |
The stored definition is corrupt or fails the designer’s checks, so it cannot be rendered | Fix the named field in the designer and save |
{
"success": false,
"error": {
"code": "PDF_TEMPLATE_NOT_FOUND",
"message": "PDF template k3d-9mp was not found."
}
}Request ID and incident ID
Every API response includes an x-request-id header (UUID). Quote it to hello@beliq.eu for faster troubleshooting.
x-request-id: 7f3d2a1b-4e5c-6d7f-8a9b-0c1d2e3f4a5bA 5xx that an endpoint in this reference raises itself additionally carries an error.incidentId in the response body. It links the response directly to the captured error event on our side, so quoting it in a support request is the fastest path to a diagnosis:
{
"success": false,
"error": {
"code": "ENGINE_UNAVAILABLE",
"message": "The e-invoice processing service is temporarily unavailable. Please try again shortly.",
"incidentId": "V7mKp2XwQd"
}
}Every DATABASE_UNAVAILABLE carries one too, whichever endpoint answers it, GET /v1/me included.
The 503s that refuse a feature that is switched off or has no provider, TRANSMISSION_DISABLED and TRANSMISSION_NO_PROVIDER, carry none: quote the x-request-id header for those.