Skip to content

Fiscalization

Every receipt sent to POST /v3/bill carries a security object. It transports the data that the fiscal regulations of the store's country require on the receipt: the TSE signature in Germany, the RKSV signature in Austria, the TicketBAI QR code in Spain, and so on. This page explains how the object is validated and which fields are mandatory for each fiscalization type. The legal background is described under Legal Requirements at the POS.

Structure of the security object

json
{
  "security": {
    "fiscalization": {
      "type": "Tse",
      "...": "type-specific fields, see below"
    },
    "extension:anybill": {
      "required": true,
      "failure": false
    }
  }
}
  • security is always mandatory, even when no fiscalization data exists for the receipt.
  • fiscalization holds the fiscal data. Its type selects the country-specific structure.
  • extension:anybill.required states whether the country requires fiscalization at all. Default true.
  • extension:anybill.failure states whether the fiscal unit failed for this transaction. Default false.

The extension also accepts publicKey and signedData. Both are only relevant for EV charging receipts and are not validated.

Which case applies?

Whether fiscalization is mandatory is decided solely by the two flags in the extension:

Caseextension:anybillfiscalizationEffect on the receipt
Regular receiptomitted, or required: true and failure: falsemandatory, validated according to its typeFiscal data is rendered
Fiscal unit unavailablefailure: trueoptional, not validatedA failure notice is rendered instead of the fiscal data; see Handling a fiscal unit failure
No fiscalization obligation (e.g. CH, NL, BE, LU)required: falseomitNothing is rendered

Omitting the extension

Omitting extension:anybill is identical to sending required: true and failure: false. Integrations that leave out the extension therefore always have to send a complete fiscalization object, which is the most common cause of unexpected TSE validation errors.

Data sent with failure: true or required: false is not validated

As soon as one of the two flags disables the requirement, the fiscalization object is skipped by the validation entirely, including a partially filled one. Signature data sent in these cases is neither checked nor rendered. With required: false omit the object; with failure: true send at most type and additionalLegalText as described below.

Handling a fiscal unit failure

A failure of the TSE, the signature creation unit or any other fiscal unit does not suspend the obligation to issue a receipt. The till keeps sending every receipt to POST /v3/bill; it only marks the affected transactions:

  1. Set security.extension:anybill.failure to true for every receipt whose transaction could not be signed. The flag describes a single transaction, not the state of the till.
  2. Omit the signature data. Optionally send a fiscalization object that contains only type and additionalLegalText to print the details of the failure (start of the outage, reason, serial number of the unit) on the receipt.
  3. As soon as the fiscal unit is available again, send receipts with failure: false (or without the flag) and a complete fiscalization object.
  4. Do not resend or correct receipts that were issued with failure: true. The outage log required by the regulations (e.g. DSFinV-K in Germany) is kept by the till, not by anybill.

Whether and how the failure is shown on the receipt depends on the seller's country in bill.head.seller.address.countryCode and on the fiscalization object that is sent:

Seller countryfiscalization sentRendered on the receipt
GermanyomittedTSE Fehlfunktion!
Germanytype: Tse with extension:anybill.additionalLegalTextTSE Fehlfunktion! followed by the additional legal text
Austriaomitted**SICHERHEITSEINRICHTUNG AUSGEFALLEN**
Austriatype: Rksv with additionalLegalText**SICHERHEITSEINRICHTUNG AUSGEFALLEN** followed by the additional legal text
Any other countryomittedNothing; add your own notice via additionalLegalText of a Tse or Rksv object or via a footer text

The notice texts for Germany and Austria are added by anybill. Do not repeat them in a footer text. The failure notice is only generated for the Tse and Rksv types; for the other types failure has no effect on the rendering.

Recommended payload for a German store during a TSE outage:

json
{
  "security": {
    "fiscalization": {
      "type": "Tse",
      "extension:anybill": {
        "additionalLegalText": "TSE ausgefallen seit 29.11.2021 14:52 Uhr, Seriennummer 623323B6...BA60B"
      }
    },
    "extension:anybill": {
      "failure": true
    }
  }
}

The type discriminator

fiscalization.type selects the structure and the validation rules. The value is case-sensitive and must be spelled exactly as listed:

typeCountry / regulationMandatory fields
TseGermany, KassenSichVserialNumber, logTimeFormat, certificate, timestampStart, timestampEnd, transactionNumber, signatureNumber, signature
RksvAustria, RKSVposInspectionQrData
BoiTvaFrance, BOI-TVAposInspectionQrData
TBaiSpain (Basque Country), TicketBAIqrCodeData, link, number, id
SwedenSweden, control unitpaymentType, fiscalNumber, controlCode
PortugalPortugal, ATCUDatcud, qrCodeData
SecurityInformationGeneric, any country without a dedicated typetitle; per data[] entry displayName, value, displayed

Two rules follow from the implementation:

  • If type is omitted, the object is treated as Tse.
  • If type carries an unknown value (including a wrong casing such as securityInformation), the object is also treated as Tse. The API then reports missing TSE fields, which is misleading. Check the spelling of type first whenever TSE errors appear for a non-German receipt.

For bill.countryCode: "FRA" the API applies additional French rules and expects the BoiTva type. When using the POS SDK, type is derived from the concrete class and does not need to be set.

Germany – TSE (Tse)

The values originate from the TSE transaction of the receipt: startTransaction() provides the start time stamp, finishTransaction() provides the end time stamp, the signature counter and the signature.

FieldRequiredConstraintsContent
serialNumberyeshexadecimal string, max. 64 charsSerial number of the TSE
logTimeFormatyesmax. 100 charsTime format used by the TSE, e.g. unixTime
certificateyesmax. 10,000 charsCertificate chain of the TSE in PEM format
timestampStartyesISO 8601 with offset, after 1900-01-01Time stamp returned by startTransaction()
timestampEndyesISO 8601 with offset, after 1900-01-01Time stamp returned by finishTransaction()
transactionNumberyesinteger ≥ 1Transaction number assigned by the TSE (0 is rejected as empty)
signatureNumberyesinteger ≥ 1Signature counter returned by finishTransaction() (0 is rejected as empty)
signatureyesSignature returned by finishTransaction()
signatureAlgorithmnomax. 64 charse.g. ecdsa-plain-SHA256
firstOrdernoISO 8601 with offset, after 1900-01-01Start time stamp of the first order transaction when a receipt closes several orders (e.g. hospitality)
processTypenomax. 100 charsProcess type passed to finishTransaction(), e.g. Kassenbeleg-V1
processDatanomax. 250 charsProcess data passed to finishTransaction(), e.g. Beleg^42.31_16.26_0.00_0.00_0.00^58.57:Bar
extension:anybill.posInspectionQrDatanomax. 1,024 charsContent of the QR code for the Kassennachschau
extension:anybill.additionalLegalTextnomax. 5,120 charsAdditional legal text printed with the fiscal data
extension:anybill.additionalTseDatanomap of entries; each displayName (max. 60 chars) and value (max. 512 chars) is mandatoryFurther key/value pairs, e.g. the cash register id
Example TSE
json
{
  "type": "Tse",
  "serialNumber": "623323B6C170DF2200...8F3A78E5BA7C4BA60B",
  "signatureAlgorithm": "ecdsa-plain-SHA384",
  "logTimeFormat": "unixTime",
  "certificate": "LS0tLS...S0tCg==",
  "timestampStart": "2021-11-29T15:00:57+01:00",
  "timestampEnd": "2021-11-29T15:00:59+01:00",
  "transactionNumber": 50,
  "signatureNumber": 121,
  "processType": "Kassenbeleg-V1",
  "processData": "Beleg^42.31_16.26_0.00_0.00_0.00^58.57:Bar",
  "signature": "AjEGS...dhRitb",
  "extension:anybill": {
    "posInspectionQrData": "V0;KASSE-01;Kassenbeleg-V1;Beleg^42.31_16.26_0.00_0.00_0.00^58.57:Bar;50;121;2021-11-29T14:00:57.000Z;2021-11-29T14:00:59.000Z;ecdsa-plain-SHA384;unixTime;AjEGS...dhRitb;BFsx...Qw==",
    "additionalTseData": {
      "cashRegisterId": {
        "displayName": "Kassen-ID",
        "value": "KASSE-01"
      }
    }
  }
}

Austria – RKSV (Rksv)

FieldRequiredConstraintsContent
posInspectionQrDatayesmax. 1,024 charsMachine-readable code of the receipt (_R1-AT1_...)
serialNumbernomax. 128 charsSerial number of the signature creation unit
transactionNumbernointeger ≥ 0Receipt number of the cash register
signatureNumbernointeger ≥ 0Signature counter
signaturenoSignature value
additionalLegalTextnomax. 5,120 charsAdditional legal text
additionalDatanomap of entries; each displayName (max. 60 chars) and value (max. 512 chars) is mandatoryFurther key/value pairs
Example RKSV
json
{
  "type": "Rksv",
  "posInspectionQrData": "_R1-AT1_4690F01D01_68_2021-10-04T13:54:19_4,00_0,00_0,00_0,00_0,00_jVWJIf9wOCDo544VtmI7dQ==_15BE7FD2_eUwHgkS8AGE=_GkWvZVamt1RKsUBSbGACKgcfhX7iNiRq4iQgZ8E4CUPHStNAnC6k7IPA5cG4OFlyAh0GsbSRanKBUfy0PsyVqg==",
  "serialNumber": "Tpzx/mesZGSu/XO6ZaKZO/bk87HKejMaU2VCDoAgmp4=",
  "transactionNumber": 1459,
  "signatureNumber": 3833,
  "signature": "AjEGS...dhRitb"
}

France – BOI-TVA (BoiTva)

Requires bill.countryCode: "FRA". France additionally expects cashRegister.version and the country-specific attributes (SIRET, NAF) in the misc extension; see the Bill Data Model.

FieldRequiredConstraintsContent
posInspectionQrDatayesmax. 1,024 charsQR code content for the inspection
additionalData[]nolistAdditional lines rendered before or after the QR code
additionalData[].typeyesText or KeyValueSelects the structure of the entry
additionalData[].sequenceNumbernointegerOrder of the entries
additionalData[].positionnoBefore or AfterPosition relative to the QR code
additionalData[].valueyesText (Text) or value (KeyValue)
additionalData[].alignmentnoLeft, Right, CenterAlignment; Text only
additionalData[].displayNameyes for KeyValueLabel of the value
Example BOI-TVA
json
{
  "type": "BoiTva",
  "posInspectionQrData": "FR;12345678900012;2024-03-01T10:15:00;58.57;...",
  "additionalData": [
    {
      "type": "KeyValue",
      "sequenceNumber": 1,
      "position": "Before",
      "displayName": "Logiciel",
      "value": "POS 4.2 – certifié NF525"
    },
    {
      "type": "Text",
      "sequenceNumber": 2,
      "position": "After",
      "alignment": "Center",
      "value": "Ticket sécurisé"
    }
  ]
}

Spain – TicketBAI (TBai)

FieldRequiredConstraintsContent
qrCodeDatayesmax. 4,096 charsContent of the TicketBAI QR code
linkyesmax. 1,024 charsTicketBAI verification URL
idyesmax. 1,024 charsTicketBAI identifier (TBAI-...)
numberyesinteger ≥ 1TicketBAI invoice number (0 is rejected as empty)
Example TicketBAI
json
{
  "type": "TBai",
  "qrCodeData": "https://tbai.example.eus/qr/?id=TBAI-00000006Y-251019-btFpwP8dcLGAF-237&s=T&nf=27174&i=5.00&cr=007",
  "link": "https://tbai.example.eus/qr/?id=TBAI-00000006Y-251019-btFpwP8dcLGAF-237",
  "id": "TBAI-00000006Y-251019-btFpwP8dcLGAF-237",
  "number": 27174
}

Sweden (Sweden)

FieldRequiredConstraintsContent
paymentTypeyesmax. 1,024 charsPayment types of the transaction, comma-separated
fiscalNumberyesmax. 1,024 charsCombination of storage location, cash register and transaction number
controlCodeyesmax. 2,048 charsControl code (digital signature) of the control unit
textnoAdditional information
Example Sweden
json
{
  "type": "Sweden",
  "paymentType": "Card",
  "fiscalNumber": "STO-01-000123-4567",
  "controlCode": "MTIzNDU2Nzg5MDEyMzQ1Ng=="
}

Portugal (Portugal)

FieldRequiredConstraintsContent
atcudyesUnique document code (ATCUD: validation code and sequential number)
qrCodeDatayesmax. 1,024 charsContent of the QR code required on Portuguese receipts
Example Portugal
json
{
  "type": "Portugal",
  "atcud": "JFF8K2VD-1234",
  "qrCodeData": "A:123456789*B:999999990*C:PT*D:FR*E:N*F:20240301*G:FR 2024/1234*H:JFF8K2VD-1234*..."
}

Generic security information (SecurityInformation)

Use this type for countries or regimes without a dedicated structure. The entries are rendered as a titled block of key/value pairs and QR codes.

FieldRequiredConstraintsContent
titleyesmax. 64 charsHeading of the block
data[]nolistEntries of the block
data[].displayNameyesmax. 64 charsLabel of the entry
data[].valueyesValue, or the QR code content for type: Qr
data[].displayedyesbooleanWhether the entry is rendered on the receipt
data[].typenoText or QrRendering of the entry; set it explicitly
data[].keynoTechnical key of the entry
data[].sequenceNumbernointegerOrder of the entries
Example SecurityInformation
json
{
  "type": "SecurityInformation",
  "title": "Fiscal data",
  "data": [
    {
      "type": "Text",
      "key": "deviceId",
      "displayName": "Device",
      "value": "FD-4711",
      "displayed": true,
      "sequenceNumber": 1
    },
    {
      "type": "Qr",
      "key": "verification",
      "displayName": "Verification",
      "value": "https://verify.example.org/?doc=4711",
      "displayed": true,
      "sequenceNumber": 2
    }
  ]
}

Receipts without fiscalization data

Fiscal unit unavailable – minimal form, see Handling a fiscal unit failure for the variant with an additional legal text:

json
{
  "security": {
    "extension:anybill": {
      "failure": true
    }
  }
}

No fiscalization obligation in the country of the store:

json
{
  "security": {
    "extension:anybill": {
      "required": false
    }
  }
}

Deprecated field names

The following names are aliases of the current fields. They behave identically and remain accepted for existing integrations, but must not be used in new code and must not be combined with the current names – if both are sent, the last one in the payload wins.

DeprecatedCurrent
security.tsesecurity.fiscalization
security.extension:anybill.tseFailuresecurity.extension:anybill.failure
security.extension:anybill.tseRequiredsecurity.extension:anybill.required

The deprecated tse property is marked read-only in the OpenAPI specification and is therefore not offered as a request field in the API reference.

Common mistakes

  • Omitting extension:anybill for a store without fiscalization obligation; the API then requires a full fiscalization object.
  • Misspelling type (wrong casing or a lowercase value). The object falls back to Tse and the API reports missing TSE fields.
  • Swapping processType and processData: processType is the short process name (Kassenbeleg-V1), processData is the Beleg^... string.
  • Sending time stamps without a time zone offset, or a placeholder date before 1900-01-01.
  • Sending the TSE serialNumber in a non-hexadecimal representation (e.g. Base64).
  • Sending additionalTseData or additionalData entries with an empty displayName or value.
  • Sending both tse and fiscalization, or both tseFailure and failure.
  • Sending placeholder or partial signature data together with failure: true; it is neither validated nor rendered. Send only type and additionalLegalText.
  • Repeating the failure notice in a footer text for German or Austrian stores; anybill adds it.
  • Setting failure: true permanently instead of per affected transaction, or keeping required: false after a temporary outage.

Built 2026-09-16 21:51 CEST from commit d540524