API documentation
Authentication and environments
OAuth 2.0 client credentials over plain TLS. One URL for everyone; sandbox and production differ by credential, not by host.
URLs
| What | Value |
|---|---|
| API | https://api.bokapos.rs |
| OpenAPI contract | https://api.bokapos.rs/openapi.yaml (public, no login) |
| Token (client credentials) | https://auth.bokapos.rs/realms/boka/protocol/openid-connect/token |
| Portal for humans | https://cloud.bokapos.rs |
There is no separate sandbox host. The same code, URLs and calls work in both environments; only the client id and secret change.
Credentials
BokaPOS Administration issues an API credential (client id and secret) at the request of the organization's Owner, separately for sandbox (boka-sbx-...) and production (boka-prod-...). The secret is shown once, at issuance, and cannot be read again; if it is lost, the credential is revoked and a new one issued. A credential is bound to one organization and one environment and carries the fixed bundle of scopes described below.
Getting a token
A standard client_credentials request, application/x-www-form-urlencoded. No scope parameter is needed; the token receives every scope of the credential.
curl -X POST "https://auth.bokapos.rs/realms/boka/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$BOKAPOS_CLIENT_ID" \
-d "client_secret=$BOKAPOS_CLIENT_SECRET"{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6...",
"expires_in": 300,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "tenant:read operations:read catalogue:read catalogue:write fiscal:read fiscal:write refund:write proforma-training:write advance:write advance:close configuration:read security-elements:read"
}- The token is valid for 300 seconds. Cache it in process memory and refresh it when, say, fewer than 30 seconds remain. Do not request a new token for every call.
- A
401on any call means an expired or invalid token: get a new one and repeat the same request (with the sameIdempotency-Key). - The token is a JWT with
org_id(your organization) andboka_env(sandboxorproduction) claims. You need not read them; the API enforces them. - A wrong client id or secret returns
401witherror: invalid_clientfrom the identity server.
Headers on every call
Authorization: Bearer <access_token>
Content-Type: application/json
Idempotency-Key: order-4127-sale-1 (fiscal mutations only)
Accept: application/jsonCredential scopes
Every API credential receives the same bundle of twelve scopes. They decide which operations it may call; every other operation in the contract belongs to the portal and the BokaPOS console.
| Scope | Allows |
|---|---|
fiscal:write | issuing Normal Sale receipts, copies and e-mail delivery |
fiscal:read | reading documents, representations, the journal, reports, deliveries, advance cases |
refund:write | refunds through /v1/refund-workflows |
advance:write | opening an advance case, advance payments, cancellations |
advance:close | closing an advance case |
proforma-training:write | proforma and training |
operations:read | operation state |
catalogue:read, catalogue:write | the product catalogue, import and export |
configuration:read | tax rates |
tenant:read | taxpayers, premises, runtime, licence |
security-elements:read | certificate metadata |
Sandbox and production
- The credential carries the environment. A sandbox credential can only work with a sandbox security element, a production credential only with a production one. Crossing them returns
403 CREDENTIAL_ENVIRONMENT_MISMATCHbefore anything is reserved. - BokaPOS assigns the sandbox element from its own pool; sandbox receipts therefore issue under BOKA GROUP DOO's PIB and verify at
sandbox.suf.purs.gov.rs. The production element is your company's certificate, uploaded by the Owner in the portal. - The sandbox is free and never blocked by licensing. Production requires a valid licence;
GET /v1/licensetells you in advance whether fiscalization is allowed. - Tax labels differ. The Tax Administration sandbox carries a generic test set; production carries the official Serbian labels. That is why labels are always read from
GET /v1/tax-rates. - A credential sees only its own environment. Every read is scoped: a sandbox key lists only sandbox taxpayers, premises, security elements and documents, a production key only production ones. An object of the other environment does not exist for you, so fetching it by
idreturns404, and setup you register (POST /v1/taxpayers) belongs to your key's environment. A tax identifier is unique per environment, so your company can exist once in sandbox and once in production. - Idempotency keys are unique per organization across both environments. Reusing a key from sandbox testing in production returns
409 IDEMPOTENCY_KEY_REUSED_IN_OTHER_ENVIRONMENT; choose a new key rather than retrying. - Same organization, both environments. The Owner sees sandbox and production documents side by side in the portal until the organization goes live; from then on the portal hides sandbox data by default (the Owner can show it again in Settings). Your system tells them apart by the credential it called with.
Moving to production is described on Going to production: certificate, activation, licence, production credential, swap in configuration. The code does not change.
Check after signing in
curl -X GET "https://api.bokapos.rs/v1/runtime" \
-H "Authorization: Bearer $BOKAPOS_TOKEN"{
"manufacturer": "BOKA GROUP DOO",
"productName": "BokaPOS",
"esirNumber": "",
"softwareVersion": "1.0.0",
"buildCommit": "1f0d454e8b2c9a7d6f5e4c3b2a1908f7e6d5c4b3",
"instanceId": "api-bokapos-rs",
"organizationId": "7c1e9a4b-2d3f-4e5a-b6c7-8d9e0f1a2b3c",
"clientId": "boka-sbx-k7m2p9x4q1wz",
"fiscalEndpointsEnabled": true,
"pfrAdapter": "configured"
}pfrAdapter: configured and fiscalEndpointsEnabled: true mean fiscalization is possible for your environment. esirNumber is empty until the Tax Administration assigns the approval number; once assigned, it prints on every receipt.