Skip to content

API documentation

Errors

Every failure has an HTTP status, a code and a clear rule about what may be retried. Here is the catalogue of codes an integrator meets, the V-PFR rejection codes and the recommendations for robust handling.

Updated: 29 August 2026 · Contract version 1.0.0

Response shapes

  • { "code": "..." } with an optional message and extra fields (invalidLabels, module, operationId): a BokaPOS rule, licence or environment.
  • An RFC 9457 problem with errors per field (application/problem+json): validation of the request shape, always 422.
  • A fiscal document with fiscalized: false (503): the V-PFR unavailable or an unknown outcome; status, failureCode and retryable say what next.
  • A workflow (refund, proforma, advance) with its own status/state and embedded documents: the error is read from the workflow state.
422: field validation (application/problem+json)
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.21",
  "title": "One or more validation errors occurred.",
  "status": 422,
  "errors": {
    "totals": [
      "The item and payment totals must match after the mandated two-decimal currency rounding."
    ]
  }
}
403: module not in the licence
{
  "code": "MODULE_NOT_LICENSED",
  "module": "advance",
  "message": "This module is not included in the organization's licence. Contact BokaPOS Administration to enable it."
}
422: label not in the fresh configuration
{
  "code": "TAX_LABEL_NOT_CURRENT",
  "invalidLabels": ["Ђ"],
  "message": "Every tax label must be present in the freshly fetched current PFR configuration."
}
503: V-PFR unavailable before submission (retryable: true)
{
  "id": "9f8e7d6c-5b4a-4321-8765-0fedcba98761",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "businessPremiseId": "b7d4e2c1-9a3f-4c8e-8f21-6e5a0c9d3b22",
  "idempotencyKey": "order-4127-sale-1",
  "clientReference": "ORDER-4127",
  "invoiceType": "NORMAL",
  "transactionType": "SALE",
  "cashierId": "web-shop",
  "buyerId": null,
  "buyerDetails": null,
  "status": "NOT_FISCALIZED",
  "fiscalized": false,
  "failureCode": "PFR_SUBMISSION_FAULT",
  "retryable": true,
  "pfr": null,
  "receipt": null,
  "createdAt": "2026-09-01T08:15:31.902Z",
  "updatedAt": "2026-09-01T08:15:32.611Z"
}

Code catalogue

The Retry column: *same key* means the same request with the same Idempotency-Key makes sense; *fix the request* means new content and a new key; *later* means the state changes outside your system (licence, settings); *never* means a retry cannot help.

HTTPCodeMeaningWhat to doRetry
401401The token is missing, expired (valid 300 seconds) or was not issued for this API.Obtain a new token with the client-credentials grant and repeat the call.same key
403CREDENTIAL_ENVIRONMENT_MISMATCHA sandbox credential is trying to use a production security element or the other way round. Rare: the other environment's taxpayers, premises and documents are invisible to your key (404), so this appears only when an element and its premise disagree.Check which credential is configured; the environment is decided by the credential, not the URL.fix the request
403CREDENTIAL_ENVIRONMENT_REQUIREDThe credential carries no environment (boka_env), so it cannot register a taxpayer. Credentials issued by BokaPOS always carry one.Use a credential issued by BokaPOS Administration; if you have one and still see this, contact support.never
403LICENSE_REQUIREDThe organization has no licence and the call targets a production element.Sandbox keeps working. For production contact BokaPOS Administration.later
403LICENSE_NOT_STARTEDThe licence exists but starts later.Read GET /v1/license for the start date.later
403LICENSE_EXPIREDThe licence has expired.Contact BokaPOS Administration. Refunds and advance cancellations keep working.later
403LICENSE_SUSPENDEDBokaPOS suspended the licence.Contact BokaPOS Administration.later
403MODULE_NOT_LICENSEDThe base licence is valid but the module in module (advance or email) is not enabled.Enable the module through BokaPOS Administration or do not use that feature in production.later
404TAXPAYER_NOT_FOUNDThe taxpayer does not exist in your organization and environment, or is not active.Read GET /v1/taxpayers with the same key and use the exact id; a taxpayer of the other environment is invisible.fix the request
404BUSINESS_PREMISE_NOT_FOUNDThe premise does not exist in your environment or does not belong to the given taxpayer.Read GET /v1/taxpayers/{taxpayerId}/business-premises.fix the request
404FISCAL_DOCUMENT_NOT_FOUNDThe document does not exist in your organization and environment.Check the identifier; documents of another organization or of the other environment are invisible.fix the request
409IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_REQUESTThe same Idempotency-Key was already used with different request content. The response carries the original's operationId.Read the original through GET /v1/operations/{operationId}. A new sale needs a new key.never
409IDEMPOTENCY_KEY_REUSED_IN_OTHER_ENVIRONMENTYour organization already used this Idempotency-Key with a credential of the other environment (for example during sandbox testing). Keys are unique per organization across both environments.Send the request again with a new key. Nothing was issued.never
422LPFR_REQUIRED_FOR_IN_PERSON_SALESThe premise is not a distance-commerce premise. BokaPOS fiscalizes only distance commerce through the V-PFR.In-person sales need an L-PFR (for example BokaLPFR).never
422BUSINESS_PREMISE_INACTIVEThe premise is suspended or closed.Activate it in the portal or use another one.later
422PAYMENT_TYPE_NOT_ALLOWED_ON_PREMISEThe premise runs in the restricted payment mode (OTHER, CASH, WIRE_TRANSFER, VOUCHER) and the request carries another type.Change the payment type or the premise's payment mode in the portal.fix the request
422ACTIVE_SECURITY_ELEMENT_REQUIREDThe premise has no active security element.In sandbox BokaPOS assigns an element; in production the Owner uploads one in the portal and BokaPOS activates it.later
422TAX_LABEL_NOT_CURRENTAt least one tax label is not in the fresh V-PFR configuration (field invalidLabels).Read GET /v1/tax-rates and use only the labels it returns; sandbox and production have different sets.fix the request
422TAX_LABEL_NOT_ALLOWED_OUTSIDE_VATThe taxpayer is marked as outside the VAT system and an item carries a VAT label.Use the non-VAT label or correct the taxpayer's VAT status in the portal.fix the request
422REFERENCE_DOCUMENT_NOT_FOUNDThe referenced document (reference.fiscalDocumentId) does not exist.Check the identifier from the original receipt's response.fix the request
422REFERENCE_DOCUMENT_NOT_FISCALIZEDThe referenced document is not fiscalized, so it cannot be a reference.A reference may point only to a FISCALIZED document.fix the request
422REFERENCE_DOCUMENT_SCOPE_MISMATCHThe referenced document belongs to another taxpayer or premise.A reference must stay on the same taxpayer and premise.fix the request
422REFERENCE_DOCUMENT_TYPE_NOT_ALLOWEDThis invoice and transaction type may not reference that kind of source document (the official reference matrix).See the reference table on the Conventions page.fix the request
422REFUND_QUANTITY_EXCEEDS_ORIGINALThe returned quantity is larger than the quantity on the original line.Lower the quantity; a partial refund is allowed.fix the request
422REFUND_CUMULATIVE_QUANTITY_EXCEEDEDThe sum of every refund of that line so far would exceed the original quantity.Check the earlier refunds in the journal.never
422REFUND_ITEM_MUST_MATCH_ORIGINAL_LINEName, price, labels or GTIN do not match the original line originalLineIndex.Copy the item from the original receipt (GET /v1/fiscal-documents/{id}/representations/canonical-json).fix the request
422REFUND_ORIGINAL_LINE_NOT_FOUNDoriginalLineIndex does not exist on the original receipt.Indexes start at zero, in the order of the original's items.fix the request
422COPY_SOURCE_NOT_COPYABLECopy, Proforma and Training documents cannot be copied.Copy only a Normal or Advance document.never
422COPY_SOURCE_NOT_FISCALIZEDThe copy's source is not fiscalized.A copy exists only for a FISCALIZED document.never
409ADVANCE_CASE_NOT_OPENThe case is closed, failed or waiting for an outcome to be resolved.Read GET /v1/advance-cases/{id} and the state field.never
422ADVANCE_CANCELLATION_TARGET_NOT_LATESTOnly the latest fiscalized Advance Sale can be cancelled.Send the id of the last advanceSales entry not listed in cancelledAdvanceSaleIds.fix the request
422ADVANCE_CLOSE_TOTAL_MISMATCHremainingPayments does not equal the final amount less the stored advance.Compute the difference from advanceSales and send it; when zero, one element with amount 0.fix the request
422ADVANCE_CASE_HAS_NO_FISCALIZED_SALEA case without any fiscalized Advance Sale cannot be closed (unless it carries externalAdvance).Fiscalize a payment first.never
409ADVANCE_PAYMENT_SUPERSEDEDA replayed request targets a payment that is no longer the latest in the chain.Read the case and continue from the current state.never
409ADVANCE_CASE_CLOSE_ALREADY_RESERVEDA closure was already reserved with a different key.Replay the closure with the same Idempotency-Key it started with.same key
503ADVANCE_REFUND_NOT_FISCALIZEDThe Tax Administration rejected the closing Advance Refund: the case is FAILED with the rejection code, and the reserved final receipt was never sent; it carries this failureCode, NOT_FISCALIZED, retryable: false.Read the case failureCode and advanceRefund.pfrRejection, fix the data and open a new case. Replaying the same closure sends nothing.never
409PROFORMA_TRAINING_WORKFLOW_RESERVATION_CONFLICTThe source document already has an unresolved or completed refund.Read the workflow from the workflowId in the response.never
409PROFORMA_TRAINING_SOURCE_ALREADY_REFUNDEDThe Proforma or Training document was already refunded in full.No further action.never
409CATALOGUE_SKU_ALREADY_EXISTSThe SKU already exists for that taxpayer.Update the existing product (PUT) or use another SKU.fix the request
422CATALOGUE_IMPORT_TOO_MANY_ROWSThe CSV has more than 1,000 rows.Split the import into several files.fix the request
422CATALOGUE_IMPORT_HEADERS_INVALIDThe CSV header lacks the expected columns.Download GET /v1/products/export as a template.fix the request
422JOURNAL_EXPORT_RESULT_LIMIT_EXCEEDEDMore than 10,000 rows match the export filters.Narrow the period (createdFrom, createdTo) and export in parts.fix the request
422RECEIPT_DELIVERY_DISABLEDThe taxpayer has not enabled e-mail delivery in the portal settings.Enable delivery in the portal (Documents, E-mail receipts) or send the receipt from your own system.later
422RECEIPT_DELIVERY_DOCUMENT_NOT_FISCALIZEDThe document is not fiscalized, so there is nothing to deliver.Send only documents with fiscalized: true.never
422RECEIPT_DELIVERY_DOCUMENT_NOT_ISSUED_TO_BUYERAn advance refund is never issued to the buyer, so BokaPOS does not send it to a buyer address. The same applies to a copy of one.Send the final receipt of the advance case (the Normal Sale). The advance refund stays available for printing and in the electronic journal.never
503RECEIPT_DELIVERY_UNAVAILABLEThe platform mail transport is not configured or unavailable. Nothing was queued.Retry later with the same key or send the receipt from your own system; fiscalization is already complete.same key
503PFR_SANDBOX_NOT_CONFIGUREDThe fiscal adapter is not configured on this deployment. The response is a document with fiscalized: false.Does not happen on api.bokapos.rs; appears only on local installations without the adapter.same key
503PFR_ENVIRONMENT_DISABLEDFiscal traffic is disabled for this element's environment (production until it is enabled).Sandbox works; production is enabled by BokaPOS decision.later
503CURRENT_TAX_CONFIGURATION_UNAVAILABLEThe V-PFR did not return a fresh tax configuration, so the request was not even reserved.Retry later with the same key.same key
503PFR_SUBMISSION_FAULTThe V-PFR was unavailable before submission; the document is NOT_FISCALIZED, retryable: true.Repeat the same request with the same Idempotency-Key after a short pause.same key
503OUTCOME_UNKNOWNThe request may have reached the V-PFR but the response did not come back. Status OUTCOME_UNKNOWN, retryable: false. Not a receipt, but it may become one.Do not send a new request for the same sale. Poll GET /v1/operations/{id}; BokaPOS resolves the outcome by reading, never by resubmitting.never
422REJECTEDThe V-PFR rejected the request. The document has status REJECTED and GET /v1/fiscal-documents/{id} returns pfrRejection with the property path and code (2310 unknown label; 2800 to 2808 required, length, range, value, format, list size).Correct the request and send it with a new key.fix the request

V-PFR rejection codes

When the V-PFR rejects a request, the document gets status REJECTED, failureCode: PFR_VALIDATION_REJECTED, and a single read (GET /v1/fiscal-documents/{id}) carries pfrRejection.items with the property path from your request and the code:

CodeMeaningTypical cause
2310unknown tax labelthe label is not in the current group (BokaPOS usually catches this earlier as TAX_LABEL_NOT_CURRENT)
2800required field missingempty name, cashier, payment
2801lengtha name, cashier or reference too long
2802rangea quantity or amount outside the allowed range
2803valuea value outside the enumeration
2804formatmore than two decimals in a price or amount, a wrong time format
2805 to 2808list size and related checksan empty item or payment list

A robust integration

  • Derive the Idempotency-Key from the order and persist it before sending, so you can repeat the same request after an interruption.
  • On 503 with retryable: true, retry with the same key and exponential back-off (for example 2, 5, 15 seconds), a few times at most; then flag the order for an operator.
  • On OUTCOME_UNKNOWN never send a new request: remember the id and poll GET /v1/operations/{id} until the status becomes FISCALIZED or an operator decides.
  • On 422 and REJECTED record errors or pfrRejection with the order, fix the data source and send with a new key.
  • On 401 get a new token and repeat the same request. On 403 read GET /v1/license.
  • Refresh tax labels when you get TAX_LABEL_NOT_CURRENT, not before every receipt.
  • Set a generous HTTP timeout on fiscal calls (for example 60 seconds): the V-PFR usually answers in about two seconds, but a connection dropped on your side turns a certain outcome into an unknown one.
  • Log id, status, failureCode and pfr.invoiceNumber; never log the token or the secret.