Skip to content

API documentation

Normal Sale receipt

The main call of the integration: the customer paid, you send the items and payments, BokaPOS issues Промет Продаја. Every request field, discounts, buyer, several payment types and receipt copies are here.

Updated: 29 August 2026 · Contract version 1.0.0

When it is issued

A Normal Sale receipt is issued when the supply happens: in distance commerce that is when the goods are delivered or the service performed, or when the payment is received, depending on your business model and the regulations. If the customer pays before delivery and delivery comes later, that is an advance. If you only send an offer, that is a proforma.

Request

POST /v1/fiscal-documents
curl -X POST "https://api.bokapos.rs/v1/fiscal-documents" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Idempotency-Key: order-4127-sale-1" \
  -H "Content-Type: application/json" \
  -d '{
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "businessPremiseId": "b7d4e2c1-9a3f-4c8e-8f21-6e5a0c9d3b22",
  "clientReference": "ORDER-4127",
  "invoiceType": "NORMAL",
  "transactionType": "SALE",
  "cashier": {
    "id": "web-shop",
    "displayName": "Web shop"
  },
  "items": [
    {
      "name": "Bluetooth slušalice",
      "unitOfMeasure": "kom",
      "quantity": 1,
      "unitPrice": 8990,
      "gtin": "8606012345678",
      "taxLabels": [
        "F"
      ]
    }
  ],
  "payments": [
    {
      "type": "CARD",
      "amount": 8990
    }
  ],
  "metadata": {
    "orderId": "4127",
    "channel": "web"
  }
}'

Fields

FieldRequiredRule
taxpayerId, businessPremiseIdyesthe taxpayer and an active distance-commerce premise
clientReferenceyesyour order number; need not be unique, but is searchable
invoiceType, transactionTypeyesalways NORMAL and SALE here
cashier.idyesthe cashier or system; displayName is optional
items[]yes, at least onesee below
payments[]yes, at least onesum equal to the item total; several payment types are allowed
buyernomandatory when the regulations require buyer identification (B2B with a PIB and other cases)
commercialFooternoyour text under the fiscal part (up to 2000 characters): thank-you note, complaints, contact
metadatanoyour key-value pairs; stored with the operation, never sent to the V-PFR, not on the receipt
referencenoonly for a copy of an external document (source: EXTERNAL); a Normal Sale has no reference

Item

FieldRequiredRule
nameyesproduct or service name, up to 2048 characters; printed as name/unit
unitOfMeasureyesunit of measure (kom, kg, h, m...), up to 50 characters; only the prescribed advance items are exempt
quantityyesup to three decimals, at least 0.001
unitPriceyesthe final gross unit price after discount, two decimals
taxLabelsyesone or more labels from GET /v1/tax-rates; usually one
gtinnoGTIN/EAN of 8 to 14 digits; printed on the receipt
catalogProductIdnothe id of a catalogue product, if you keep one in BokaPOS; items may also be fully inline
unitPriceBeforeDiscountnothe pre-discount price, must be greater than unitPrice; shown outside the fiscal part

Response

201 Created
{
  "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": "FISCALIZED",
  "fiscalized": true,
  "failureCode": null,
  "retryable": false,
  "pfr": {
    "invoiceNumber": "JWX4K9PL-JWX4K9PL-1042",
    "sdcTime": "2026-09-01T10:15:32.483+02:00",
    "requestedBy": "JWX4K9PL",
    "signedBy": "JWX4K9PL",
    "transactionTypeCounter": 897,
    "totalCounter": 1042,
    "invoiceCounterExtension": "ПП",
    "totalAmount": 8990,
    "totalTax": 890.9009,
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "journal": "============ ФИСКАЛНИ РАЧУН ============\n115711881\nBOKA GROUP DOO\nBokaPOS sandbox\nРоза Луксембург 16\nБеоград-Раковица\nКасир:                          web-shop\nЕСИР број:                    1656/1.0.0\n-------------ПРОМЕТ ПРОДАЈА-------------\nАртикли\n========================================\nНазив   Цена         Кол.         Укупно\nBluetooth slušalice/kom (F)\n       8.990,00          1       8.990,00\n----------------------------------------\nУкупан износ:                   8.990,00\nПлатна картица:                 8.990,00\n========================================\nОзнака       Име      Стопа        Порез\nF           ECAL   11,00%         890,90\n----------------------------------------\nУкупан износ пореза:              890,90\n========================================\nПФР време:          01.09.2026. 10:15:32\nПФР број рачуна:      JWX4K9PL-JWX4K9PL-1042\nБројач рачуна:                 897/1042ПП\n========================================\n======== КРАЈ ФИСКАЛНОГ РАЧУНА ========="
  },
  "receipt": {
    "textUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/official-text",
    "jsonUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/canonical-json",
    "pdfA4Url": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/pdf-a4",
    "pdf80mmUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/pdf-80mm",
    "pdf58mmUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/pdf-58mm",
    "previewImageUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/preview-png",
    "qrImageUrl": "/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/representations/qr-svg",
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "preferredPaperFormat": "a4"
  },
  "createdAt": "2026-09-01T08:15:31.902Z",
  "updatedAt": "2026-09-01T08:15:32.611Z"
}

If you send the same request again (same body, same Idempotency-Key) after a 201, you get 200 with the same document and the same id: the receipt is not issued twice. A new receipt exists only after the first 201 with fiscalized: true; a 200 only confirms the one already issued.

FieldMeaning
idthe document identifier in BokaPOS: store it with the order; it serves copies, refunds, representations and delivery
status, fiscalizedFISCALIZED and true mean the receipt exists
pfr.invoiceNumberthe official receipt number JID-JID-counter
pfr.sdcTimethe official receipt time (V-PFR signature)
pfr.verificationUrlthe Tax Administration verification link; hand it to the customer as is or as a QR code
pfr.journalthe official receipt text (journal); may be shown or printed as is
pfr.totalAmountthe total amount the V-PFR signed
pfr.totalTaxthe total tax the V-PFR signed (the sum of the per-label tax amounts); may be null
pfr.totalCounter, pfr.transactionTypeCounter, pfr.invoiceCounterExtensionthe signed counters and the official document-type mark (ПП, ПР, АП, АР...); they may be null, so accept that; the counter is always in the journal too
receipt.*URLs of the seven representations; see Receipt representations and delivery
buyerDetailsthe buyer's name and address from the NBS register when the buyer is a domestic company; otherwise null

Buyer, discount and several payment types

The same call covers a sale to a company with a PIB (buyer.id with prefix 10:), an item discount (unitPriceBeforeDiscount for display only, unitPrice is what gets fiscalized), decimal quantities, several items and a split between wire transfer and card.

POST /v1/fiscal-documents
curl -X POST "https://api.bokapos.rs/v1/fiscal-documents" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Idempotency-Key: order-4128-sale-1" \
  -H "Content-Type: application/json" \
  -d '{
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "businessPremiseId": "b7d4e2c1-9a3f-4c8e-8f21-6e5a0c9d3b22",
  "clientReference": "ORDER-4128",
  "invoiceType": "NORMAL",
  "transactionType": "SALE",
  "cashier": {
    "id": "web-shop"
  },
  "buyer": {
    "id": "10:106952811"
  },
  "items": [
    {
      "name": "Godišnja licenca",
      "unitOfMeasure": "kom",
      "quantity": 2,
      "unitPrice": 12000,
      "taxLabels": [
        "F"
      ]
    },
    {
      "name": "Instalacija",
      "unitOfMeasure": "h",
      "quantity": 1.5,
      "unitPrice": 4000,
      "unitPriceBeforeDiscount": 5000,
      "taxLabels": [
        "F"
      ]
    }
  ],
  "payments": [
    {
      "type": "WIRE_TRANSFER",
      "amount": 20000
    },
    {
      "type": "CARD",
      "amount": 10000
    }
  ],
  "commercialFooter": "Hvala na kupovini. Reklamacije: podrska@primer.rs"
}'
201 Created
{
  "id": "8e7d6c5b-4a39-4210-8765-fedcba987650",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "businessPremiseId": "b7d4e2c1-9a3f-4c8e-8f21-6e5a0c9d3b22",
  "idempotencyKey": "order-4128-sale-1",
  "clientReference": "ORDER-4128",
  "invoiceType": "NORMAL",
  "transactionType": "SALE",
  "cashierId": "web-shop",
  "buyerId": "10:106952811",
  "buyerDetails": {
    "legalName": "PRIMER DOO BEOGRAD",
    "taxIdentifier": "106952811",
    "registrationNumber": "20712345",
    "address": "Bulevar kralja Aleksandra 1",
    "city": "Beograd",
    "source": "nbs-jrr",
    "resolvedAt": "2026-09-01T08:20:11.004Z"
  },
  "status": "FISCALIZED",
  "fiscalized": true,
  "failureCode": null,
  "retryable": false,
  "pfr": {
    "invoiceNumber": "JWX4K9PL-JWX4K9PL-1043",
    "sdcTime": "2026-09-01T10:15:32.483+02:00",
    "requestedBy": "JWX4K9PL",
    "signedBy": "JWX4K9PL",
    "transactionTypeCounter": 898,
    "totalCounter": 1043,
    "invoiceCounterExtension": "ПП",
    "totalAmount": 30000,
    "totalTax": 2972.973,
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "journal": "============ ФИСКАЛНИ РАЧУН ============\n...\nПФР број рачуна:      JWX4K9PL-JWX4K9PL-1043\nБројач рачуна:                898/1043ПП\n========================================\n======== КРАЈ ФИСКАЛНОГ РАЧУНА ========="
  },
  "receipt": {
    "textUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/official-text",
    "jsonUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/canonical-json",
    "pdfA4Url": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/pdf-a4",
    "pdf80mmUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/pdf-80mm",
    "pdf58mmUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/pdf-58mm",
    "previewImageUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/preview-png",
    "qrImageUrl": "/v1/fiscal-documents/8e7d6c5b-4a39-4210-8765-fedcba987650/representations/qr-svg",
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "preferredPaperFormat": "a4"
  },
  "createdAt": "2026-09-01T08:15:31.902Z",
  "updatedAt": "2026-09-01T08:15:32.611Z"
}

A discount is always part of the price: the V-PFR has no discount field, so BokaPOS sends unitPrice as the final price and shows the difference only outside the fiscal part. Spread an order-level discount across the items before sending.

What to store in your system

  • the document id and the Idempotency-Key you used (so you can repeat the same request after an interruption);
  • pfr.invoiceNumber, pfr.sdcTime and pfr.verificationUrl (for the customer, accounting and complaints);
  • the PDF if you wish, but you need not: representations stay available at the receipt.* URLs and carry an ETag.

Receipt copy

Копија is the official document that repeats a fiscalized receipt (Normal or Advance, sale or refund) when the customer asks for another copy. BokaPOS builds it from the stored original; you send only the cashier. The copy is signed as a new document, prints ОВО НИЈЕ ФИСКАЛНИ РАЧУН and references the original. A refund copy carries the customer-signature line. Proforma, Training and Copy documents cannot be copied.

POST /v1/fiscal-documents/{fiscalDocumentId}/copies
curl -X POST "https://api.bokapos.rs/v1/fiscal-documents/9f8e7d6c-5b4a-4321-8765-0fedcba98761/copies" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Idempotency-Key: order-4127-copy-1" \
  -H "Content-Type: application/json" \
  -d '{
  "cashier": {
    "id": "web-shop"
  }
}'
201 Created
{
  "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "businessPremiseId": "b7d4e2c1-9a3f-4c8e-8f21-6e5a0c9d3b22",
  "idempotencyKey": "order-4127-copy-1",
  "clientReference": "COPY-ORDER-4127",
  "invoiceType": "COPY",
  "transactionType": "SALE",
  "cashierId": "web-shop",
  "buyerId": null,
  "buyerDetails": null,
  "status": "FISCALIZED",
  "fiscalized": true,
  "failureCode": null,
  "retryable": false,
  "pfr": {
    "invoiceNumber": "JWX4K9PL-JWX4K9PL-1044",
    "sdcTime": "2026-09-01T10:15:32.483+02:00",
    "requestedBy": "JWX4K9PL",
    "signedBy": "JWX4K9PL",
    "transactionTypeCounter": 899,
    "totalCounter": 1044,
    "invoiceCounterExtension": "КП",
    "totalAmount": 8990,
    "totalTax": 890.9009,
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "journal": "============ ФИСКАЛНИ РАЧУН ============\n...\nПФР број рачуна:      JWX4K9PL-JWX4K9PL-1044\nБројач рачуна:                899/1044КП\n========================================\n======== КРАЈ ФИСКАЛНОГ РАЧУНА ========="
  },
  "receipt": {
    "textUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/official-text",
    "jsonUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/canonical-json",
    "pdfA4Url": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/pdf-a4",
    "pdf80mmUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/pdf-80mm",
    "pdf58mmUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/pdf-58mm",
    "previewImageUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/preview-png",
    "qrImageUrl": "/v1/fiscal-documents/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e/representations/qr-svg",
    "verificationUrl": "https://sandbox.suf.purs.gov.rs/v/?vl=A0pXWDRLOVBMSldYNEs5UEwSBAAAEAQAAKCLPAAAAAAAAAABnAqJa1EAAAA...",
    "preferredPaperFormat": "a4"
  },
  "reference": {
    "fiscalDocumentId": "9f8e7d6c-5b4a-4321-8765-0fedcba98761",
    "pfrNumber": "JWX4K9PL-JWX4K9PL-1042",
    "pfrTime": "2026-09-01T10:15:32.483+02:00"
  },
  "createdAt": "2026-09-01T08:15:31.902Z",
  "updatedAt": "2026-09-01T08:15:32.611Z"
}

Repeating the same request with the same Idempotency-Key returns 200 with the copy already issued; no new copy is signed.

What does not go through this call

  • Refund: POST /v1/refund-workflows, because it verifies the original lines and issues the automatic copy for cash (Refund).
  • Advance: /v1/advance-cases, because it is a chain of documents with references (Advance).
  • Proforma and Training: /v1/proforma-training-workflows (Proforma and Training).
  • In-person sales: they require an L-PFR; a premise that is not distance commerce returns 422 LPFR_REQUIRED_FOR_IN_PERSON_SALES.

Sale errors

HTTPCodeMeaningWhat to doRetry
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
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
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