For developers

Fiscalization from your server, without hardware

Tezga is a cloud ESIR that works with the Tax Administration's virtual processor of fiscal receipts (V-PFR). Your platform calls the REST API, we issue and fiscalize the receipt and return the PFR number, QR and PDF. There is no local installation, no device and no card readers.

IB of the approved ESIR
1597IB of the approved ESIR
no local hardware
V-PFRno local hardware
server-to-server
RESTserver-to-server

Your first receipt

curl -X POST https://pos.narbiz.com/api/integrations/order?provider=custom \
  -H "X-Tezga-Api-Key: tzg_…" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "NALOG-1042",
    "tok": "fiskalni",
    "nacinPlacanja": "Kartica",
    "kupac": { "naziv": "Kupac", "email": "kupac@primer.rs" },
    "stavke": [
      { "naziv": "Paket od 10 časova", "kolicina": 1, "cenaSaPdv": 24000 }
    ]
  }'

The response carries fiskalni.pfrBroj, brojacRacuna, verifikacioniUrl and javniPdfUrl, which works without signing in.

46 questions integrators send us most often

How it works

Is Tezga a cloud ESIR, with no local installation or hardware?
For integration, yes: your server calls our API, we fiscalize through the Tax Administration's virtual processor of fiscal receipts (V-PFR) and return the receipt data. No local software and no hardware on your side. Tezga also supports a local processor (L-PFR) for taxpayers who sell in physical premises, because the law requires them to have a device that issues a receipt even when the internet connection drops; that is a separate mode and changes nothing in the API integration.
Is there a direct REST API, independent of the ready connectors?
There is, and it is the primary way of working, not an add-on. The WooCommerce and Shopify connectors use the same API. The machine-readable description is at /api/openapi.json (Postman, Swagger), and the human guide is at /api-dokumentacija.pdf.
How is a request authenticated?
With an API key in the X-Tezga-Api-Key header. The key has the form tzg_ followed by 48 hexadecimal characters; the test and production keys look the same, because the environment is a property of the account (see the question about a guard in code). You can have several keys, one per integration, and each is paused or revoked separately. Each key can be bound to your server's IP addresses (individual ones or a CIDR range), so a stolen key does not work from anywhere else. Optionally, the body is additionally signed with an HMAC-SHA256 secret (X-Tezga-Signature). We do not offer OAuth or mTLS.
Which operations does the API cover?
Issuing a fiscal receipt and an e-invoice, a pro forma invoice that closes itself on payment, an advance invoice and closing an advance, cancellation and refund (full and by line), a receipt copy, a document's status with all its fiscal data, the account's tax labels and bank transactions.
What do I get in the response when a receipt is issued?
The PFR number, the time, the receipt counter, the ESIR number, the verification URL and the QR content, plus the public receipt link and a PDF that works without signing in. Enough to print the receipt yourself or send it to the customer.

Reliability

What happens when the Tax Administration's V-PFR is unavailable?
The document is issued and not lost, and the response is HTTP 202 with ok:false and a message. Tezga then retries the fiscalization itself at intervals and, once it goes through, notifies you by webhook; you can also read the status yourself through GET /documents/:id. The exception is an uncertain outcome (the Tax Administration received the receipt but the write on our side did not succeed): that is not retried automatically, because the state would otherwise receive a second receipt for the same turnover. Working without an internet connection on the V-PFR path does not exist, because the virtual processor works over a constant connection by definition; a taxpayer who needs that uses a local processor (L-PFR) in their premises.
How is a duplicate receipt prevented?
Every request carries your externalId. The same externalId never creates a second receipt but returns the existing one, even when two requests arrive at the same time. The protection is in the API itself, not in the connector.
Are there webhook notifications?
There are. Tezga sends a POST to your address for these events: racun.fiskalizovan, racun.greska, refundacija.izvrsena, storno.izvrsen. The body is HMAC-signed, and if your server does not respond, delivery is retried after 1, 5, 15, 60, 360 minutes.
Is there a request rate limit?
By default 120 requests per minute per company for issuing and refunds, and 240 for reads. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a 429 carries Retry-After. The limit is tied to the account, not to the code: if you have an announced peak, for example the start of ticket sales for a large event, tell us the expected number of receipts per minute and we raise it before the sale begins.
Under which approval does Tezga operate?
Tax Administration decision 000-092-00-00123/2026-0000 of 03.07.2026, IB 1597, version 1.1, as an ESIR that works with V-PFR.

Distance selling and tickets

What does the flow look like for distance selling, for example tickets?
The customer pays by card at their payment provider, your backend waits for the payment confirmation and only then sends a single POST to /api/integrations/order with your own order number. We issue and fiscalize the receipt and, in the same response, return the PFR number, the counter, the QR and a link to the PDF. We either send the receipt to the customer, or you take it from the public link and embed it in your purchase confirmation.
I sell on behalf of an event organiser; how does that work with you?
Technically: the receipt always carries the tax ID (PIB) of the company whose account it is, because it is issued with that company's security element. If your platform is the seller, everything fits into one account. If each organiser issues their own receipt, then each organiser is their own account, with their own security element and their own subscription, and your system simply chooses the key it sends the order with. Which of the two of you is the issuer of the receipt is not our decision but a matter of your contract with the organiser and your accountant's opinion; we support both arrangements.
How is a refund done for a cancelled ticket?
POST /api/integrations/refund, by our documentId or by your order number. When no money is returned (a receipt issued in error), use "vrsta":"storno", which does not require customer details. For a refund of money, full or by line, use "vrsta":"refundacija". Customer identification is required only when cash is returned, as the Tax Administration prescribes: it is taken from the original, and when it is missing the fields "kupacIdBroj" and "kupacIdTip" arrive (ID card, foreign passport, foreign ID card); otherwise 422 with a clear message. A refund to a card, to an account or by other non-cash means (PayPal) does not require customer details.
Does the customer have to get a paper receipt?
No. In distance selling the receipt is delivered electronically: we email it to the customer if you send posaljiMejl:true, or you take it as a PDF from the public link in the response and show it in the customer's account and in your confirmation. The link works without signing in and without an API key, so it can go straight into an email.

Security element and accounts

Is my security element used, or do you require a new one?
Yours is used, the one the Tax Administration has already issued to you, in the form of a .pfx file. You submit it yourself in Settings, Fiscalization (file, password and PAK); it is stored encrypted and never leaves the server. We do not need a smart card, which is for hardware devices.
How do you handle several portals or several business premises?
Several portals under the same business premises work through the same company, each with its own API key, at no extra licence cost. A separate business premises with its own security element is a separate company in the account, with its own subscription; switching is one click.
What does testing before production look like?
Testing is done on the Tax Administration's official sandbox environment, with a test security element it issues. In Settings you enter the sandbox address and the test certificate, and moving to production is a swap of the address and the certificate. The API key and your code stay the same. A test account with no element at all runs in simulation: the receipt is created locally, the PFR number starts with MOCK and nothing goes to the Tax Administration.
How do I prevent a test from issuing a real receipt in my code?
At startup, call GET /api/integrations/tax-labels and read the field okruzenje: "produkcija" means real receipts at the Tax Administration, "sandbox" the Tax Administration test environment, "simulacija" a local simulation without the Tax Administration. A test configuration may continue only if the value is not "produkcija". A second check is on every response: a real receipt has a verifikacioniUrl at https://suf.purs.gov.rs/v/, a sandbox receipt on the subdomain sandbox.suf.purs.gov.rs, and a simulated PFR number that starts with MOCK. Keep the test and production keys in separate configurations, because they do not differ in shape.
How are tax rates handled if I am not in the VAT system?
The account is marked as a non-payer and Tezga records every line as exempt, regardless of the rate you send. What the account uses you can also read through GET /api/integrations/tax-labels.

Price and timelines

Which plan is required for direct API access?
Direct API, outgoing webhooks and the two-way connection with a WooCommerce and Shopify shop are in the Biznis plan (2,390 RSD a month). On the other plans, including Start, an API request gets a 402 response with an explanation. A company with the Enterprise tier enabled also has the connection, on any plan, and a company that used an API or web shop connection before 16 September 2026 keeps it on its plan. A key for the register terminal on an Android device is not an integration and works on every plan.
Is there a contractual obligation or a notice period?
Neither. The subscription is paid in advance for three, six or twelve months and does not renew itself. If you do not extend, access ends at expiry, and the data stays available for export.
How long does the integration take?
To the first test receipt, usually half a working day with a developer ready: one call to issue, one to check the status, one to cancel. Access to the environment is the same day, and moving to production is a settings change, not new development.
What is on you, and what is on me?
On us: activation, correct fiscalization under the Tax Administration's decision, documentation and support during the integration. On you: the account and payment, submitting the security element and the PAK, your code and the decision to move to production.

Receipt data from the web shop and ERP

Is the whole receipt content sent, or only the total amount?
The whole content, per line: name, code, quantity, unit of measure, price with VAT and tax rate for each line separately. The total is not sent, Tezga calculates it from the lines, so the sum and the lines can never disagree. One receipt carries up to 500 lines. If the code exists in Tezga's catalogue, the line is linked to that item, so the report by item works for web sales too.
How do discounts and promo codes from the shop show on the fiscal receipt?
As a discount, not as a hidden price. On /order you send popustProcenat or popustRsd per line, and popustProcenat or popustRsd on the whole receipt (only one field per level). Tezga spreads a whole-receipt discount across the lines in proportion to the base, just as in the app, so VAT by tax rate stays correct, and the remaining penny goes to the largest line. When a line carries a code from Tezga's catalogue, send cenaIzZahteva:true: then the price and discount from the request apply, and the name, unit of measure and rate come from the catalogue. Web shop connections (WooCommerce, Shopify, PrestaShop, OpenCart, Magento, BigCommerce) always fiscalize the price the customer paid. A discount larger than the amount, two fields at the same level, and a discount together with a catalogue code without cenaIzZahteva return 422.
How are the 20 and 10 percent rates recorded when the cart is mixed?
Each line carries its own rate, so a mixed cart is not a special case. Tezga works with the Tax Administration's three tax labels: Ђ for 20 percent, Е for 10 percent and А for exempt. In the API you send the rate as 20, 10, 0 or pdv20, pdv10, oslobodjeno; for the web shop connector you set up a mapping of the shop's tax class to one of those three. An empty rate is read as 20 percent, never as exempt, and an unknown value returns an error instead of a wrong receipt. A zero from the platform is not treated as exempt for a VAT payer.
Does the system recognise whether the customer is a private individual or a company?
By the tax ID (PIB). If a PIB arrives with the order, the customer is recorded as a company: the fiscal receipt carries the customer identification 10:PIB, and the customer is found or created in the register by their PIB. Without a PIB the receipt is retail. The connectors take the PIB from the platform's fields: Magento from customer_taxvat, PrestaShop from siret, WooCommerce from the meta field for PIB or VAT, Shopify from the order note in the form PIB: 123456789. From your own system you simply send it in the field kupac.pib.
Does a B2B receipt from the web shop also go to SEF, or does the ERP do that?
It can go from Tezga. For an order with a PIB, the fiskalni_i_sef flow issues a fiscal receipt and sends the same document to SEF as an e-invoice, with the fiscal PFR number as a reference on the invoice. The sef flow sends only the e-invoice, without fiscalization. The condition is that the company has its own SEF API key in Settings; without it the document gets the status not connected and waits. A public-sector buyer with a JBKJS cannot go into the fiscal flow, because they are issued exclusively an e-invoice.
How does an advance invoice work when the customer pays by card up front?
Through the dedicated address /api/integrations/advance: you send the advance amount, the payment date, the rate and the customer, and Tezga issues an advance invoice. When you deliver the goods, the same call with zatvori:true and the real lines issues a refund of the advance and then the final invoice, with a reference to the advance that the regulation requires. If the refund of the advance does not go through at the Tax Administration, the final invoice is not issued, so no duplicate turnover arises. A web order paid in advance can also go through /order with tok:"avans" (and an optional datumUplate): an advance invoice is created for the order total after the discount, and it is closed with the same /advance call with zatvori:true and the same externalId. That works for an order with a single tax rate; mixed rates return 422 with instructions for /advance. For an order that is paid and delivered at once no advance is needed: a single call to /order.
Does a partial refund work, for example one of three items?
It does. POST /api/integrations/refund with vrsta:refundacija and a list of lines with quantities returns only those lines, and the amount is calculated in proportion to the original and reduced by whatever was returned earlier. A cancellation (storno) is always the whole receipt. A refund carries the number and time of the original receipt in the PFR, so the link is visible to the Tax Administration too. The original is found by Tezga's documentId or by your order number.
How is an order cancelled when the customer backs out, from the ERP or from the shop?
From the ERP with a single call to /api/integrations/refund by order number. From Tezga with a button on the order: a fiscal cancellation or refund is issued, the order gets the status refunded, and a refund record and a note are written into WooCommerce, a note into Shopify. The money is not returned to the customer automatically, that stays with your payment provider. A cancellation and a refund issued through the API, once they go through at the Tax Administration, trigger a webhook with the order number; actions from the register do not send an event, so the ERP reads them through GET documents.

Receipt to the customer from the web shop

How does a web shop customer get the fiscal receipt?
In two ways, your choice. First: Tezga emails the customer itself as soon as the receipt goes through at the Tax Administration, if an address arrived with the order and the field posaljiMejl:true, or if automatic sending is enabled in the connection with the shop. Second: in the API response you get the public receipt link and a PDF that work without signing in and without a key, so you embed them in your purchase confirmation or in the customer's account. The email is not sent if the fiscalization did not go through, so the customer does not receive a receipt the Tax Administration does not have.
Is the receipt a PDF attachment or only a verification link?
Both. The email carries the receipt PDF as an attachment, and on the receipt itself there is a QR code and the address suf.purs.gov.rs for verification at the Tax Administration. You also get the verification URL in the API response, so you can display it yourself. Every receipt also has a public link that works without signing in, used for sharing and a copy.
Can the email with the receipt carry my company name and details?
It can. The email header shows the company logo, the sender is shown under the company name, the customer's reply goes to your address, and the footer carries the name, address, phone and website. The logo is embedded in the email itself, not loaded from the internet; a PNG, JPG or GIF up to 200 KB is shown in the email, and without a logo the initials of the name are shown. You change the intro text and the subject in Settings, Emails, Receipt to the customer. The receipt PDF is attached.

Scale, availability and connecting systems

How much load does the API take, and can it be raised?
By default 120 requests per minute for issuing and refunds, and 240 for reads, per company, not per key or per IP address. The limit is an account setting, not code: it is raised to 10,000 requests per minute on request, and the lowest is 10. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so your system knows how much it may still send. The number of fiscal receipts per month is not limited: 3,000 are included, above that 2 dinars per receipt, and sales do not stop.
What availability and monitoring do you have, when the shop runs non-stop?
The system status is public, without signing in, at /status: the database, fiscalization with the percentage of successful receipts in the last 24 hours, email sending and background jobs, with a list of current and the last ten incidents. Monitoring checks V-PFR, SUF, SEF, email and the database every 10 minutes. When the Tax Administration does not respond, all users see a bar with instructions, and Tezga retries the fiscalization itself every 10 minutes, up to 12 times in 48 hours, and after the last failure opens an urgent ticket and emails the company. A contractual service level with a guaranteed response time exists at the Enterprise tier, in three steps: Standard (a 4-hour response on working days), Extended (2 hours, 7 days a week, an on-call phone 08 to 22) and Critical turnover (30 minutes, 0 to 24, round-the-clock on-call cover with a named engineer).
Which e-commerce platforms do you already work with?
WooCommerce and Shopify have a full two-way connection: Tezga pulls orders itself every 30 minutes and on a button, fiscalizes by the status you choose (paid, shipped or completed) and writes the receipt number back into the order. PrestaShop, OpenCart, Magento and Adobe Commerce, and BigCommerce work through an incoming webhook. WordPress without WooCommerce, Wix, Gomag, a custom site and headless shops work through the REST API; n8n, Make and Zapier through the same API. For WooCommerce there is also an add-on that shows the fiscal receipt next to the order in the admin, read-only.
How do items from the ERP get into Tezga?
Through the API, automatically. POST /api/integrations/products takes up to 500 items per call (code, name, price with VAT, tax rate, unit of measure, GTIN, active), and POST /api/integrations/customers up to 500 customers (name, PIB with a check-digit validation, company number, JBKJS, address, email, phone). An item is recognised by its code, a customer by PIB, then email, then name; nothing is deleted, and a field you do not send stays as it is. One bad line does not break the rest, and a repeated call is safe. Stock levels are not written through the API, so the change log stays accurate. From the shop the catalogue is copied automatically (WooCommerce, Shopify), and manually there is an import from Excel or CSV.
How many integrations may there be on one account?
Up to 20 named API keys per company, one per integration, with pausing and with allowed IP addresses per key; a read-only key cannot issue a receipt. Up to 5 webhook addresses per company, each with its own secret. Every API call is logged with the key, the route and the outcome, so you can see which integration does what.

Roles, data and updates

How are you billed: per API call, per receipt or a fixed fee?
A fixed monthly subscription per plan, paid in advance for 3, 6 or 12 months, with no fixed-term contract and no onboarding fee. API calls are not billed. Fiscal receipts are included up to 3,000 a month, above that 2 dinars per receipt. For chains, marketplace platforms and systems with their own development team there is an Enterprise tier, with a price on request.
What does support outside working hours look like?
Regular support works on business days, by email, phone, WhatsApp and Viber, and the people who write the software answer. Reports are handled as tickets with a first-response deadline: urgent 4 hours, normal 24 hours. When the Tax Administration is down, the system recognises it itself, notifies all users and retries the fiscalization without waiting for support. On-call cover at weekends and on holidays is arranged at the Enterprise tier: an on-call phone 08 to 22, or round-the-clock cover.
Where is the data, and who sees the security element?
The app and the database run in the European Union, in Frankfurt; the processors are Vercel, Neon, Cloudflare and Resend, listed in the Privacy Policy. The certificate, password, PAK and PIN are stored encrypted with AES-256-GCM, are never shown back either to you or to us, and are used only in communication with the processor of fiscal receipts. API keys are stored as a SHA-256 hash and are shown only once, at creation. Access to the account can be restricted to the company's IP addresses, sign-in has two-factor protection, and every administrative action stays in the log.
How many users are there, and what roles?
Three roles: administrator, manager and cashier. Cashiers work under their own logins, so each receipt carries the name of whoever issued it, and takings are seen by shift and by cashier. The Tezga and Start plans have one user, and from the Posao plan the number of users is not limited. An employee gets an email invitation and sets their own password; the link is valid for 72 hours and is used once. In the Biznis plan one account runs several companies, switching in one click. An accountant is added as a contact and receives the scheduled reports themselves, without entering the register. Every user action stays in the activity log.
Who archives the fiscal receipts, and how long are they kept?
Tezga, in the database, with each receipt and the Tax Administration's verification link. Fiscal records are not deleted by a setting or by deleting the account: a company that ever issued a fiscal receipt is anonymised, and the receipts remain because of the legal period of 10 years. You take all the company's data in one click as a ZIP, from all tables, without secrets. With the digital archive add-on (DMS Fascikla) every fiscalized receipt also goes into an e-archive in PDF/A form, with an archive book and retention periods.
What happens when the Tax Administration changes the rules; does an update break the site?
It does not. Tezga runs in the cloud, is updated on our side, and there is no installation on yours: a new version is published as a whole and switched over in a single moment, while the previous one stays ready to roll back in a few seconds. Database schema changes are additive only, so old and new code work over the same database. Tezga is approved as ESIR 1597 version 1.1; the Tax Administration's technical instructions (TaxCore) are followed and built in by us, and your API contract does not change. The correctness of the connection to V-PFR is checked every night.
Can I test the integration before I pay?
There is no trial period, but testing is not conditional on paying for real receipts. On request we open a demo account for 48 hours, with sample data, in which receipts do not go to the Tax Administration: the PFR number starts with MOCK, and the field okruzenje in tax-labels returns simulacija. With your own test security element you work on the Tax Administration's official sandbox environment. Moving to production is a swap of the address and the certificate; the API key and your code stay the same.

Something not answered?

Write to us with a description of your scenario (what you sell, who your customers are, how they pay) and we will answer concretely, with call examples for your case.

podrska@tezgafiskal.com