Skip to content

Reference

Catalogue

Products and services with price, unit of measure, GTIN and tax labels; CSV import and export.

6 operations

Read a product

get/v1/products/{productId}

Scope catalogue:read

One product by identifier.

Parameters

getProduct: Parameters
FieldInTypeDescription
productIdrequiredpathuuid

Example

GET /v1/products/{productId}
curl -X GET "https://api.bokapos.rs/v1/products/d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN"

Responses

getProduct: Responses
200 OKProductProduct
404 Not FoundResource does not exist within the authenticated tenant scopeErrorCode
200 OK
{
  "id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "sku": "BT-HP-001",
  "gtin": "8606012345678",
  "name": "Bluetooth slušalice",
  "unitOfMeasure": "kom",
  "grossUnitPrice": 8990,
  "taxLabels": [
    "F"
  ],
  "isActive": true,
  "createdAt": "2026-08-22T10:00:00.000Z",
  "updatedAt": "2026-08-22T10:00:00.000Z"
}
Response: Product

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

getProduct: Response
FieldTypeDescription
idrequireduuid
taxpayerIdrequireduuid
skurequiredstring
gtinoptionalstring| null (nullable)
namerequiredstring
unitOfMeasureoptionalstring| null (nullable)
grossUnitPricerequirednumber

>= 0

taxLabelsrequiredarray<string>
isActiverequiredboolean
createdAtrequireddate-time
updatedAtrequireddate-time

Update a product

put/v1/products/{productId}

Scope catalogue:write

Replaces every editable field. The taxpayer is immutable; deactivation preserves the historical snapshots on receipts.

Contract notes (OpenAPI)

The owning taxpayer is immutable; deactivation preserves historical receipt snapshots.

Parameters

updateProduct: Parameters
FieldInTypeDescription
productIdrequiredpathuuid

Request body application/json

updateProduct: Request body
FieldTypeDescription
skurequiredstring

min 1max 100

gtinoptionalstring| null (nullable)

max 32

namerequiredstring

min 1max 500

unitOfMeasurerequiredstring

min 1max 50

grossUnitPricerequirednumber

>= 0step 0.01

taxLabelsrequiredarray<string>

min 1 itemsunique

isActiverequiredboolean

Example

PUT /v1/products/{productId}
curl -X PUT "https://api.bokapos.rs/v1/products/d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "sku": "BT-HP-001",
  "gtin": "8606012345678",
  "name": "Bluetooth slušalice Pro",
  "unitOfMeasure": "kom",
  "grossUnitPrice": 9490,
  "taxLabels": [
    "F"
  ],
  "isActive": true
}'

Responses

updateProduct: Responses
200 OKUpdated productProduct
404 Not FoundResource does not exist within the authenticated tenant scopeErrorCode
409 ConflictThe requested resource conflicts with an existing tenant-scoped recordConflict
422 Unprocessable ContentThe command violates a Boka or fiscal ruleErrorCode
200 OK
{
  "id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "sku": "BT-HP-001",
  "gtin": "8606012345678",
  "name": "Bluetooth slušalice Pro",
  "unitOfMeasure": "kom",
  "grossUnitPrice": 9490,
  "taxLabels": [
    "F"
  ],
  "isActive": true,
  "createdAt": "2026-08-22T10:00:00.000Z",
  "updatedAt": "2026-09-04T12:00:00.000Z"
}
Response: Product

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

updateProduct: Response
FieldTypeDescription
idrequireduuid
taxpayerIdrequireduuid
skurequiredstring
gtinoptionalstring| null (nullable)
namerequiredstring
unitOfMeasureoptionalstring| null (nullable)
grossUnitPricerequirednumber

>= 0

taxLabelsrequiredarray<string>
isActiverequiredboolean
createdAtrequireddate-time
updatedAtrequireddate-time

Import the catalogue (CSV)

post/v1/products/import

Scope catalogue:write

All or nothing: at most 1,000 rows or 5 MB, labels separated by |. Serbian spreadsheet CSV (BOM, sep=;, decimal comma) is accepted too.

Contract notes (OpenAPI)

The import is all-or-nothing, accepts at most 1,000 rows or 5 MB, and uses `|` between tax labels.

Parameters

importProducts: Parameters
FieldInTypeDescription
taxpayerIdrequiredqueryuuid

Request body text/csv

text/csv

Example

POST /v1/products/import
curl -X POST "https://api.bokapos.rs/v1/products/import?taxpayerId=3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Content-Type: text/csv" \
  --data-binary @katalog.csv

Responses

importProducts: Responses
200 OKImport countsProductImportResult
404 Not FoundResource does not exist within the authenticated tenant scopeErrorCode
409 ConflictThe requested resource conflicts with an existing tenant-scoped recordConflict
422 Unprocessable ContentThe command violates a Boka or fiscal ruleErrorCode
200 OK
{
  "created": 1,
  "updated": 1,
  "total": 2
}
Response: ProductImportResult

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

importProducts: Response
FieldTypeDescription
createdrequiredinteger

>= 0

updatedrequiredinteger

>= 0

totalrequiredinteger

>= 1<= 1000

Export the catalogue (CSV)

get/v1/products/export

Scope catalogue:read

A deterministic CSV of one taxpayer, the same format as the import.

Parameters

exportProducts: Parameters
FieldInTypeDescription
taxpayerIdrequiredqueryuuid

Example

GET /v1/products/export
curl -X GET "https://api.bokapos.rs/v1/products/export?taxpayerId=3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  --output katalog.csv

Responses

exportProducts: Responses
200 OKCatalogue CSV using invariant decimals and `|`-separated tax labels
404 Not FoundResource does not exist within the authenticated tenant scopeErrorCode
200 OK · text/csv
sku,name,gtin,unitOfMeasure,grossUnitPrice,taxLabels,isActive
BT-HP-001,Bluetooth slušalice,8606012345678,kom,8990.00,F,true
SRV-INST,Instalacija,,h,4000.00,F,true
Response: ErrorCode

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

exportProducts: Response
FieldTypeDescription
coderequiredstring
messageoptionalstring

List products

get/v1/products

Scope catalogue:read

The catalogue per taxpayer, searchable by name, SKU or GTIN with an active filter. Pages through the cursor.

Parameters

listProducts: Parameters
FieldInTypeDescription
taxpayerIdoptionalqueryuuid
searchoptionalquerystring

max 200

isActiveoptionalqueryboolean
cursoroptionalqueryuuid
pageSizeoptionalqueryinteger

>= 1<= 200default 50

Example

GET /v1/products
curl -X GET "https://api.bokapos.rs/v1/products?taxpayerId=3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11&search=slu%C5%A1alice&isActive=true" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN"

Responses

listProducts: Responses
200 OKProduct pageProductPage
400 Bad RequestThe command violates a Boka or fiscal ruleErrorCode
422 Unprocessable ContentThe command violates a Boka or fiscal ruleErrorCode
200 OK
{
  "items": [
    {
      "id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
      "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
      "sku": "BT-HP-001",
      "gtin": "8606012345678",
      "name": "Bluetooth slušalice",
      "unitOfMeasure": "kom",
      "grossUnitPrice": 8990,
      "taxLabels": [
        "F"
      ],
      "isActive": true,
      "createdAt": "2026-08-22T10:00:00.000Z",
      "updatedAt": "2026-08-22T10:00:00.000Z"
    }
  ],
  "nextCursor": null
}
Response: ProductPage

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

listProducts: Response
FieldTypeDescription
itemsrequiredarray<Product>
idrequireduuid
taxpayerIdrequireduuid
skurequiredstring
gtinoptionalstring| null (nullable)
namerequiredstring
unitOfMeasureoptionalstring| null (nullable)
grossUnitPricerequirednumber

>= 0

taxLabelsrequiredarray<string>
isActiverequiredboolean
createdAtrequireddate-time
updatedAtrequireddate-time
nextCursorrequireduuid| null (nullable)

Add a product

post/v1/products

Scope catalogue:write

A product or service with SKU, name, unit of measure, gross price (two decimals) and tax labels. Receipt lines may, but need not, reference the catalogue.

Request body application/json

createProduct: Request body
FieldTypeDescription
taxpayerIdrequireduuid
skurequiredstring

min 1max 100

gtinoptionalstring| null (nullable)

max 32

namerequiredstring

min 1max 500

unitOfMeasurerequiredstring

min 1max 50

grossUnitPricerequirednumber

>= 0step 0.01

taxLabelsrequiredarray<string>

min 1 itemsunique

Example

POST /v1/products
curl -X POST "https://api.bokapos.rs/v1/products" \
  -H "Authorization: Bearer $BOKAPOS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "sku": "BT-HP-001",
  "gtin": "8606012345678",
  "name": "Bluetooth slušalice",
  "unitOfMeasure": "kom",
  "grossUnitPrice": 8990,
  "taxLabels": [
    "F"
  ]
}'

Responses

createProduct: Responses
201 CreatedProductProduct
409 ConflictThe requested resource conflicts with an existing tenant-scoped recordConflict
422 Unprocessable ContentThe command violates a Boka or fiscal ruleErrorCode
201 Created
{
  "id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
  "taxpayerId": "3f9c2a8e-6b1d-4e5a-9c47-1d2b8e6f0a11",
  "sku": "BT-HP-001",
  "gtin": "8606012345678",
  "name": "Bluetooth slušalice",
  "unitOfMeasure": "kom",
  "grossUnitPrice": 8990,
  "taxLabels": [
    "F"
  ],
  "isActive": true,
  "createdAt": "2026-08-22T10:00:00.000Z",
  "updatedAt": "2026-08-22T10:00:00.000Z"
}
409 Conflict
{
  "code": "CATALOGUE_SKU_ALREADY_EXISTS",
  "message": "A product with this SKU already exists for the taxpayer."
}
Response: Product

Field descriptions come from the OpenAPI contract; field names and values are identical in both languages.

createProduct: Response
FieldTypeDescription
idrequireduuid
taxpayerIdrequireduuid
skurequiredstring
gtinoptionalstring| null (nullable)
namerequiredstring
unitOfMeasureoptionalstring| null (nullable)
grossUnitPricerequirednumber

>= 0

taxLabelsrequiredarray<string>
isActiverequiredboolean
createdAtrequireddate-time
updatedAtrequireddate-time