Skip to content

invalid_request

HTTP 400, 404 or 409 type: validation_error | not_found | conflict
{
"error": {
"type": "validation_error",
"code": "invalid_request",
"message": "total must be a decimal with exactly 2 places, e.g. \"12.00\". Received \"12.5\".",
"param": "total",
"upstream": null,
"doc_url": "https://docs.fiskhub.com/errors/invalid_request"
}
}

What happened

The catch-all code, used when a request is wrong in a way that has no more specific name. The type and param narrow it down, and message names the actual problem.

Nothing was fiscalized and nothing was stored.

Common causes on POST /v1/invoices:

Causeparam
total sent as a JSON number (4.5) instead of a string ("4.50")total
An amount with one decimal or three ("4.5", "4.500")total
occurred_at without an explicit UTC offsetoccurred_at
A required field missingthat field
A tax line missing rate, base or amountvat.0.rate etc.
currency other than EURcurrency
special_purpose longer than 1000 charactersspecial_purpose

Elsewhere in the API it also covers:

SituationHTTP
No such invoice, device or company404
A duplicate OIB, device label or ISU number at one premises409
A premises_code that is not 1–20 letters and digits400
Attempting to change an immutable field — a company’s oib, a device’s company_id or premises_code400
Retrying a confirmed invoice409
A malformed CSV or certificate upload400

Should you retry?

Not unchanged. The request is wrong and will keep being wrong. Fix what param and message point at, then send again — with the same Idempotency-Key, since nothing was fiscalized.

The fix

Read param, then message. Between them they name the field and what is wrong with it.

The two that catch integrators most often:

Money is a string with exactly two decimals.

{ "total": 4.5 } // wrong — a JSON number
{ "total": "4.5" } // wrong — one decimal
{ "total": "4.50" } // right

Binary floating point cannot represent cents exactly, and the ZKI is a hash over the concatenated string form — 4.5 and 4.50 produce different, and therefore wrong, protection codes.

Time carries an explicit offset.

{ "occurred_at": "2026-08-27 10:32:11" } // wrong — not ISO 8601
{ "occurred_at": "2026-08-27T10:32:11" } // wrong — no offset
{ "occurred_at": "2026-08-27T10:32:11+02:00" } // right

Without an offset the sale moment is ambiguous, and the sale moment determines both the fiscal record and the 30-day deadline.

A note on generic 404s and 409s

Some not_found and conflict responses carry this code rather than a specific one, because the condition is ordinary rather than diagnostic — an id that does not exist, a label already taken. Read type to tell them apart, and message for the detail.