{"openapi":"3.1.0","info":{"title":"Tezga eKasa - public API","version":"1.6.0","description":"Fiscalisation from your server: issuing invoices, cancellation and refund, writing products and customers from an ERP, document status, tax labels. Cloud ESIR with the Tax Administration's V-PFR, no local installation. The API is part of the Posao and Biznis plans; a company on a lower plan gets 402. Field names, enum values and error messages are in Serbian. Human guide: /api-dokumentacija-en.pdf (version 1.0, updated 30.10.2026.).","contact":{"name":"Go Simple","email":"podrska@tezgafiskal.com"}},"servers":[{"url":"https://pos.narbiz.com"}],"security":[{"ApiKljuc":[]}],"tags":[{"name":"Invoices","description":"Issuing, cancellation and refund."},{"name":"Goods","description":"Kalkulacija (goods receipt) and nivelacija (price change)."},{"name":"Catalogue","description":"Writing products and customers from an ERP."},{"name":"Reading","description":"Document status and account settings."},{"name":"Webhook","description":"What Tezga sends to your server."}],"components":{"securitySchemes":{"ApiKljuc":{"type":"apiKey","in":"header","name":"X-Tezga-Api-Key","description":"The key is created in Settings → Integrations and has the form tzg_ plus 48 hexadecimal characters. Test and production keys look the same: the environment is a property of the account, read from the okruzenje field in GET /api/integrations/tax-labels. Optionally the body is also signed with HMAC (X-Tezga-Signature)."}},"schemas":{"Dokument":{"type":"object","properties":{"documentId":{"type":"string","format":"uuid"},"broj":{"type":"string"},"tip":{"type":"string"},"statusDokumenta":{"type":"string","enum":["nacrt","izdat","storniran"]},"ukupnoSa":{"type":"string"},"nacinPlacanja":{"type":"string","nullable":true},"referentniDokumentId":{"type":"string","format":"uuid","nullable":true},"fiskalni":{"type":"object","nullable":true,"description":"Mandatory elements of a fiscal receipt; null = the document is not fiscalised.","properties":{"pfrBroj":{"type":"string"},"pfrVreme":{"type":"string","format":"date-time"},"brojacRacuna":{"type":"string"},"esirBroj":{"type":"string"},"verifikacioniUrl":{"type":"string"},"qrPayload":{"type":"string"},"referentniBroj":{"type":"string","nullable":true},"referentnoVreme":{"type":"string","format":"date-time","nullable":true}}},"javniUrl":{"type":"string","description":"Public invoice page, works without login."},"javniPdfUrl":{"type":"string","description":"Invoice PDF, works without login."},"pdfUrl":{"type":"string","description":"Legacy field: requires login."},"verifikacioniUrl":{"type":"string","nullable":true}}},"Fiskalni":{"type":"object","nullable":true,"description":"Mandatory elements of a fiscal receipt; null = the document is not fiscalised.","properties":{"pfrBroj":{"type":"string"},"pfrVreme":{"type":"string","format":"date-time"},"brojacRacuna":{"type":"string"},"esirBroj":{"type":"string"},"verifikacioniUrl":{"type":"string"},"qrPayload":{"type":"string"},"referentniBroj":{"type":"string","nullable":true},"referentnoVreme":{"type":"string","format":"date-time","nullable":true}}},"Greska":{"type":"object","properties":{"error":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}}}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Allowed per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining in the window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the new window."}}},"paths":{"/api/integrations/order":{"post":{"tags":["Invoices"],"summary":"Issue an invoice (or a pro forma)","description":"Idempotent by externalId: a repeated request returns the existing document (status “postojalo”), never a second invoice. When the PFR rejects, the document is issued and the response is 202 with ok:false.","parameters":[{"name":"provider","in":"query","schema":{"type":"string","enum":["woocommerce","shopify","prestashop","opencart","magento","bigcommerce","custom"],"default":"custom"},"description":"Body shape; “custom” is the direct contract below."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["externalId","stavke"],"properties":{"externalId":{"type":"string","maxLength":200,"description":"Your unique order number, up to 200 characters. The same number never creates a second invoice."},"tipDokumenta":{"type":"string","enum":["racun","predracun"],"description":"Default “racun” (invoice). “predracun” (pro forma) also requires pozivNaBroj."},"tok":{"type":"string","enum":["sef","fiskalni","fiskalni_i_sef","avans"],"description":"What gets created: an eFaktura on SEF, a fiscal receipt, or both. Without the customer's PIB (tax ID) always fiscal. “avans”: an order paid in advance, delivered later; АВАНС ПРОДАЈА is created for the order total after discounts (same path as POST /advance), and it is closed through POST /advance with zatvori:true and the same externalId. Only one tax rate in the order (otherwise 422); it does not go with a pro forma or posaljiNaSef."},"datumUplate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Only with tok “avans”: the day the payment landed. Default today (Belgrade)."},"posaljiNaSef":{"type":"boolean","description":"Automatic sending to SEF for tok “sef”."},"fiskalizujPredracun":{"type":"boolean","description":"Also fiscalise the pro forma itself (Proforma). Only with a fiscal flow."},"posaljiMejl":{"type":"boolean","description":"Email the PDF to the customer."},"model":{"type":"string","description":"Reference number model (such as “97”)."},"pozivNaBroj":{"type":"string","description":"Payment reference number; required with a pro forma."},"nacinPlacanja":{"type":"string","description":"Free text; printed on the invoice as sent, and one of the seven official types goes to the Tax Administration. Recognised: gotovina (cash), kartica (card), ček (cheque), instant (also IPS), vaučer (voucher), prenos/virman/uplatnica/račun (bank transfer); “Drugo bezgotovinsko” (such as PayPal) and any unrecognised text go as other non-cash. Default “Kartica”."},"popustProcenat":{"type":"number","minimum":0,"description":"Discount on the WHOLE invoice in percent. It is split across line items in proportion to their amounts (like an invoice discount in the app), so VAT per rate stays correct. Not together with popustRsd (422)."},"popustRsd":{"type":"number","minimum":0,"description":"Discount on the WHOLE invoice in dinars, distributed as for popustProcenat. Larger than the line items total returns 422. Not together with popustProcenat."},"cenaIzZahteva":{"type":"boolean","description":"true: for a line whose code exists in the catalogue, the price and discount from the request apply, and the link to the product, name, unit of measure and tax rate come from the catalogue; the catalogue discount is not added. Omitted or false (provider “custom”): price, rate and discount from the catalogue, as before, and a discount in the request on such a line returns 422. For provider woocommerce, shopify, prestashop, opencart, magento and bigcommerce the price from the shop always applies."},"kupac":{"type":"object","description":"Name, PIB (tax ID, for B2B), email, and optionally address, city, postal code and company number. Without a PIB it is a sale to an individual. When a PIB exists, Tezga keeps the customer record by it, so the address and company number sent stay with the customer and appear on the document.","properties":{"naziv":{"type":"string"},"pib":{"type":"string"},"email":{"type":"string","format":"email"},"adresa":{"type":"string"},"mesto":{"type":"string"},"postanskiBroj":{"type":"string"},"maticniBroj":{"type":"string"}}},"stavke":{"type":"array","minItems":1,"description":"Invoice line items. A known code takes the name and rate from the catalogue, and the price and discount from the catalogue unless cenaIzZahteva: true.","items":{"type":"object","required":["naziv","kolicina","cenaSaPdv"],"properties":{"naziv":{"type":"string"},"sifra":{"type":"string"},"kolicina":{"type":"number"},"cenaSaPdv":{"type":"number","description":"Unit price INCLUDING VAT."},"popustProcenat":{"type":"number","minimum":0,"maximum":100,"description":"Line discount in percent, as in the invoice editor. Over 100 returns 422. Not together with popustRsd."},"popustRsd":{"type":"number","minimum":0,"description":"Discount on the whole row in dinars (not per piece): the line amount is quantity × price minus popustRsd. Larger than the row returns 422. Not together with popustProcenat."},"jedinicaMere":{"type":"string","maxLength":20,"description":"Unit of measure on the invoice, such as “kom”, “usluga”, “h”, “kg”. Default “kom”."},"poreskaStopa":{"type":"string","description":"“20”, “10”, “0” or “oslobodjeno” (“pdv20”, “pdv10”, “10%”, “0.1”, “exempt”, “zero” also work). An unknown value returns an error; 20% is not assumed. For a flat-rate taxpayer it is always set to 0."}}}}}}}}},"responses":{"200":{"description":"Invoice issued (or already existed).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Dokument"},{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["kreirano","postojalo"]},"sef":{"type":"object","nullable":true},"avansId":{"type":"string","format":"uuid","description":"Only with tok “avans”: the advance id (for closing)."},"avansBroj":{"type":"string","description":"Only with tok “avans”: the advance contract number."}}}]}}}},"202":{"description":"Document issued, fiscalisation failed (ok:false, fiskalGreska)."},"400":{"description":"The body is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"401":{"description":"The key is missing or invalid."},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"409":{"description":"Only with tok “avans”: the externalId was already issued as a document that is not an advance, the advance is being processed in parallel, or the advance rules do not allow it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"413":{"description":"Body over 512 KB."},"422":{"description":"The shape is valid, but the rules do not allow it: both discount fields at the same level, a discount larger than the line or invoice amount, a discount with a catalogue code without cenaIzZahteva: true. Nothing was issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"429":{"description":"Limit exceeded (see Retry-After)."}}}},"/api/integrations/refund":{"post":{"tags":["Invoices"],"summary":"Cancellation or refund","description":"A fiscalised invoice is not changed: a NEW document is created that refers to the original. When the PFR rejects, 202; for a cancellation or full refund a repeated request sends the same refund, and do not repeat a refund by line items (Tezga retries fiscalisation by itself).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["vrsta"],"properties":{"documentId":{"type":"string","format":"uuid","description":"The ID from the order response."},"externalId":{"type":"string","maxLength":200,"description":"Or your order number instead of the ID."},"provider":{"type":"string","enum":["woocommerce","shopify","prestashop","opencart","magento","bigcommerce","custom"],"description":"With externalId; default “custom”."},"vrsta":{"type":"string","enum":["storno","refundacija"],"description":"Storno = an invoice issued by mistake; refundacija = money returned."},"razlog":{"type":"string","maxLength":500},"kupacIdBroj":{"type":"string","description":"The number of the customer's ID document. Required only for a refund of a CASH payment when the original has no customer identification; a repayment of a non-cash payment (card, bank transfer, other non-cash) does not require it. The type is set by “kupacIdTip”."},"kupacIdTip":{"type":"string","enum":["10","11","12","13","14","15","16","20","21","22","23","30","31","32","33","34","35","36","40"],"description":"Document code from the Tax Administration's list; default “20” (ID card). Most common: “20” ID card, “30” foreigner's passport, “23” Serbian passport, “36” EU ID card, “10” PIB, “11” JMBG. For “10”, “11”, “14”, “15” and “20” digits only (10 and 14 exactly 9, 11 and 15 exactly 13, 20 from 6 to 12), for “12” the form PIB:JBKJS, other types accept letters and digits. Code, colon and number together at most 20 characters."},"stavke":{"type":"array","description":"Only with a refund: a partial repayment by line items.","items":{"type":"object","required":["redosled","kolicina"],"properties":{"redosled":{"type":"string","description":"The line's ordinal on the original; the first is “0”."},"kolicina":{"type":"number"}}}}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["kreirano","postojalo"]},"vrsta":{"type":"string","enum":["storno","refundacija"]},"original":{"type":"object"},"refundacija":{"$ref":"#/components/schemas/Dokument"}}}}}},"202":{"description":"Refund created, the PFR did not accept it."},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"404":{"description":"The original was not found in your company."},"409":{"description":"The rules do not allow it (not issued, partly refunded, nothing left)."},"422":{"description":"Invalid request or kupacIdBroj is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}}}}},"/api/integrations/advance":{"post":{"tags":["Invoices"],"summary":"Advance invoice and closing an advance","description":"Without “zatvori”: an advance is created and АВАНС ПРОДАЈА is issued for the amount actually received, with the payment date. With “zatvori”: true, АВАНС РЕФУНДАЦИЈА and then ПРОМЕТ ПРОДАЈА for the full amount are created; if the refund does not pass the PFR, the final invoice is NOT issued. Idempotent by externalId.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["externalId","placeniAvans","datumUplate"],"properties":{"externalId":{"type":"string","maxLength":200,"description":"Your unique number, up to 200 characters; it prevents a duplicate advance."},"kupac":{"type":"object","properties":{"naziv":{"type":"string"},"pib":{"type":"string"},"email":{"type":"string","format":"email"}}},"placeniAvans":{"type":"number","description":"The advance amount ACTUALLY received; the fiscal receipt is issued for it."},"ugovoreniIznos":{"type":"number","description":"Contract value; by default equal to the advance paid."},"datumUplate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The day the payment landed on the account. Mandatory by law."},"poreskaStopa":{"type":"string","enum":["pdv20","pdv10","oslobodjeno"]},"nacinPlacanja":{"type":"string","description":"How the advance was paid: Gotovina (cash), Kartica (card), Prenos (bank transfer), Ček (cheque), Vaučer (voucher), Instant plaćanje (instant payment), Drugo bezgotovinsko (other non-cash). Without it “Drugo”."},"posaljiMejl":{"type":"boolean","description":"Send the advance invoice (PDF with a QR code) to kupac.email once the Tax Administration accepts it. Only with the customer's consent (Rulebook on types of fiscal invoices, Art. 13(2))."},"napomena":{"type":"string","maxLength":500,"description":"Description of the job (such as “Šišanje i feniranje”). It goes on the final invoice when the advance is closed without line items."}}},{"type":"object","required":["zatvori"],"properties":{"zatvori":{"type":"boolean","enum":[true]},"externalId":{"type":"string","description":"The advance's externalId."},"avansId":{"type":"string","format":"uuid"},"punIznos":{"type":"number","description":"Full amount of the final invoice. Not needed with line items (their total)."},"stavke":{"type":"array","minItems":1,"description":"What was actually sold (services and goods, each with its own rate). The final invoice shows them all, and the advance is closed by reference. Without line items: one row with the job description (“napomena” from issuing the advance) for the full amount; without a description, closing returns an error, because the law requires the name of the goods or service.","items":{"type":"object","required":["naziv","kolicina","cenaSaPdv"],"properties":{"naziv":{"type":"string"},"kolicina":{"type":"number"},"cenaSaPdv":{"type":"number","description":"Unit price INCLUDING VAT."},"jedinicaMere":{"type":"string","description":"Default “kom”."},"poreskaStopa":{"type":"string","description":"“20”, “10”, “0” or “oslobodjeno” (“exempt”, “zero” also work)."}}}},"nacinPlacanja":{"type":"string","description":"How the remainder above the advance was paid (Gotovina, Kartica, Prenos...)."},"provider":{"type":"string","maxLength":40,"description":"With externalId: where the advance was issued. “advance” for POST /advance, the order's provider (such as “custom”) for an advance from POST /order with tok “avans”. Without it, “advance” is tried, then “custom”."}}}]}}}},"responses":{"200":{"description":"Advance issued, or advance closed with a final invoice."},"202":{"description":"Advance invoice issued, the PFR did not accept it."},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"404":{"description":"Advance not found."},"409":{"description":"The rules do not allow it (no fiscal advance, full amount lower than the advance, the refund did not pass the PFR)."},"422":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}}}}},"/api/integrations/kalkulacija":{"post":{"tags":["Goods"],"summary":"Kalkulacija (goods receipt)","description":"Receive goods and release prices to the register with no manual work: the engine writes selling prices, increases stock and writes a KEP row, all in one transaction. Products are matched BY CODE from the catalogue, and the price is CALCULATED (purchase price + margin, or selling price). Idempotent by externalId. “Roba” feature (Biznis plan).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["externalId","stavke"],"properties":{"externalId":{"type":"string","maxLength":200,"description":"Your unique number; it prevents a duplicate document on a repeated request."},"datum":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date the goods were received. Without it, today."},"dobavljac":{"type":"object","description":"Law on Trade, Art. 29: it is saved ON the document. If the PIB exists among your customers, the document is linked and empty fields are filled in.","properties":{"naziv":{"type":"string"},"pib":{"type":"string"},"maticni":{"type":"string"},"adresa":{"type":"string"}}},"dokumentBroj":{"type":"string","maxLength":100,"description":"Number of the incoming document (invoice, delivery note), Art. 29."},"dokumentDatum":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of the incoming document."},"napomena":{"type":"string","maxLength":1000},"stavke":{"type":"array","minItems":1,"maxItems":500,"description":"Products are matched BY CODE from the catalogue (name, unit of measure and tax rate are read from the catalogue, not from the request). The price is CALCULATED by the engine: send the purchase price and EXACTLY ONE of “marzaProcenat”, “prodajnaCenaBezPdv” or “prodajnaCenaSaPdv”. An unknown code is 422.","items":{"type":"object","required":["sifra","kolicina","nabavnaCena"],"properties":{"sifra":{"type":"string","maxLength":100},"kolicina":{"type":"number","description":"Quantity received. Negative is allowed (a cancellation is a new document)."},"nabavnaCena":{"type":"number","description":"Purchase price per unit, EXCLUDING VAT."},"zavisniTrosak":{"type":"number","description":"Transport, customs and the like: the TOTAL on the line, excluding VAT."},"marzaProcenat":{"type":"number","description":"Price authority (one of three)."},"prodajnaCenaBezPdv":{"type":"number","description":"Price authority (one of three)."},"prodajnaCenaSaPdv":{"type":"number","description":"Price authority (one of three)."}}}}}}}}},"responses":{"200":{"description":"Kalkulacija posted (status “kreirano” or “postojalo”)."},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"409":{"description":"A request with this externalId is already being processed."},"413":{"description":"Body over 512 KB."},"422":{"description":"Invalid request, or the code is not in the catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"429":{"description":"Write limit exceeded (see Retry-After)."}}}},"/api/integrations/nivelacija":{"post":{"tags":["Goods"],"summary":"Nivelacija (price change)","description":"Change a product's selling price and write the difference to the KEP (increase = debit, decrease = credit). Products are matched BY CODE; the request carries only the new price, and the engine reads the old price and the quantity on hand from the catalogue at the moment of posting. Idempotent by externalId. “Roba” feature (Biznis plan).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["externalId","stavke"],"properties":{"externalId":{"type":"string","maxLength":200,"description":"Your unique number; it prevents a duplicate document."},"datum":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of the price change. Without it, today."},"povod":{"type":"string","enum":["redovna","akcija_pocetak","akcija_kraj"],"description":"Why the price changes. Default “redovna”. “popust_na_kasi” does NOT go through the API (it is a proposal from an issued invoice)."},"razlog":{"type":"string","maxLength":500},"stavke":{"type":"array","minItems":1,"maxItems":500,"description":"Products are matched BY CODE. The request carries ONLY the new price; the engine reads the old price and the quantity on hand from the catalogue at the moment of posting. An unknown code is 422.","items":{"type":"object","required":["sifra","cenaNovaSaPdv"],"properties":{"sifra":{"type":"string","maxLength":100},"cenaNovaSaPdv":{"type":"number","description":"New selling price INCLUDING VAT."}}}}}}}}},"responses":{"200":{"description":"Nivelacija posted (status “kreirano” or “postojalo”)."},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"409":{"description":"A request with this externalId is already being processed."},"413":{"description":"Body over 512 KB."},"422":{"description":"Invalid request, or the code is not in the catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"429":{"description":"Write limit exceeded (see Retry-After)."}}}},"/api/integrations/copy":{"post":{"tags":["Invoices"],"summary":"Invoice copy (КОПИЈА)","description":"A new document that mirrors the original and refers to it. It goes to the PFR ONLY if the original was fiscalised; the naPfr field in the response says so. Without the Idempotency-Key header every call creates a new copy; with the same key a repeated call returns the same one (status “postojalo”).","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":100}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"documentId":{"type":"string","format":"uuid"},"externalId":{"type":"string","maxLength":200,"description":"Or the order number of the original."},"provider":{"type":"string","description":"With externalId; default “custom”."}}}}}},"responses":{"200":{"description":"Copy created."},"202":{"description":"Copy created, the PFR did not accept it."},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"404":{"description":"Original not found."},"409":{"description":"A draft cannot be copied."},"422":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}}}}},"/api/integrations/products":{"post":{"tags":["Catalogue"],"summary":"Write products (1 to 500)","description":"A product is looked up by code, and a product without a code by name; one that is found is changed, a new one is created, nothing is deleted. A field you do not send stays as it is. Stock levels are NOT accepted (the fields stanje, zaliha, zalihe, kolicina, pratiZalihu, minStanje return an item error): stock changes only through the stock ledger. One bad item does not break the others. Repeating the same request is safe (status bez_izmene).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["stavke"],"properties":{"stavke":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"object","required":["sifra","naziv","cenaSaPdv"],"additionalProperties":false,"properties":{"sifra":{"type":"string","maxLength":100,"description":"The product code in your ERP; the write key. The same code (regardless of case) changes the same product and never creates a second one."},"naziv":{"type":"string","maxLength":200,"description":"Name as it goes on the invoice. It changes the name of an existing product."},"cenaSaPdv":{"type":"number","minimum":0,"description":"Unit price INCLUDING VAT. It also accepts text (“1990.00”, “1990,50”). A change goes into the product's price history."},"poreskaStopa":{"type":"string","description":"Same as on /order: “20”, “10”, “0” or “oslobodjeno” (“pdv20”, “pdv10”, “10%”, “0.1”, “exempt”, “zero” also work); empty text is 20%, an unknown value is an item error. Omitted: a new product gets 20%, an existing one keeps its own."},"jedinicaMere":{"type":"string","maxLength":20,"description":"Such as “kom”, “kg”, “l”, “usluga”. A new product without it gets “kom”."},"gtin":{"type":"string","pattern":"^\\d{8,14}$","description":"Barcode (GTIN), 8 to 14 digits."},"aktivan":{"type":"boolean","description":"false hides the product from the register; nothing is deleted. A new product is active."}}}}}},"example":{"stavke":[{"sifra":"KAF-01","naziv":"Espresso","cenaSaPdv":180,"poreskaStopa":"20","jedinicaMere":"kom"},{"sifra":"HL-05","naziv":"Hleb beli 500 g","cenaSaPdv":75,"poreskaStopa":"10"}]}}}},"responses":{"200":{"description":"Request processed; the outcome is per item.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"true when no item is an error."},"ukupno":{"type":"object","properties":{"kreiran":{"type":"integer"},"izmenjen":{"type":"integer"},"bez_izmene":{"type":"integer"},"greska":{"type":"integer"}}},"stavke":{"type":"array","items":{"type":"object","properties":{"indeks":{"type":"integer","description":"Position in the array sent, from 0."},"sifra":{"type":"string","nullable":true},"status":{"type":"string","enum":["kreiran","izmenjen","bez_izmene","greska"],"description":"kreiran = new; izmenjen = found and changed; bez_izmene = found, same data; greska = this item was not written (see poruka), the others were."},"id":{"type":"string","format":"uuid","nullable":true},"nadjenPo":{"type":"string","nullable":true},"poruka":{"type":"string","nullable":true}}}}}}}}},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"401":{"description":"The key is missing or invalid, or the HMAC signature is invalid."},"402":{"description":"The subscription is not active or the credit is used up."},"403":{"description":"A read-only key, a register terminal key, or an IP address outside the key's list."},"413":{"description":"Body over 512 KB."},"422":{"description":"No array, it is empty or has more than 500 items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"429":{"description":"Write limit exceeded (see Retry-After)."}}}},"/api/integrations/customers":{"post":{"tags":["Catalogue"],"summary":"Write customers (1 to 500)","description":"A customer is looked up by PIB, then by email, then by name; an entry with a PIB is matched by email and name only to a customer without a PIB. One that is found is updated (name and the fields you send), a new one is created, nothing is deleted; an empty field does not overwrite stored data. One bad item does not break the others.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kupci"],"properties":{"kupci":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"object","required":["naziv"],"additionalProperties":false,"properties":{"naziv":{"type":"string","maxLength":200,"description":"Company name or first and last name."},"pib":{"type":"string","description":"9 digits with a valid check digit. The first write key. A customer with a PIB (tax ID) is a legal entity."},"maticniBroj":{"type":"string","pattern":"^\\d{8}$","description":"Company registration number, 8 digits; PIB is required with it."},"jbkjs":{"type":"string","pattern":"^\\d{5}$","description":"JBKJS of a public funds user, 5 digits. For such a customer /order issues an eFaktura, not a fiscal receipt."},"email":{"type":"string","format":"email","description":"The second write key (when the PIB does not exist or was not found)."},"telefon":{"type":"string","maxLength":40},"adresa":{"type":"string","maxLength":200},"mesto":{"type":"string","maxLength":100},"postanskiBroj":{"type":"string","pattern":"^\\d{5}$"}}}}}},"example":{"kupci":[{"naziv":"Primer doo","pib":"100200309","maticniBroj":"12345678","adresa":"Bulevar 1","mesto":"Novi Sad","postanskiBroj":"21000","email":"racuni@primer.rs"}]}}}},"responses":{"200":{"description":"Request processed; the outcome is per customer.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"true when no item is an error."},"ukupno":{"type":"object","properties":{"kreiran":{"type":"integer"},"izmenjen":{"type":"integer"},"bez_izmene":{"type":"integer"},"greska":{"type":"integer"}}},"kupci":{"type":"array","items":{"type":"object","properties":{"indeks":{"type":"integer","description":"Position in the array sent, from 0."},"pib":{"type":"string","nullable":true},"naziv":{"type":"string","nullable":true},"status":{"type":"string","enum":["kreiran","izmenjen","bez_izmene","greska"],"description":"kreiran = new; izmenjen = found and changed; bez_izmene = found, same data; greska = this item was not written (see poruka), the others were."},"id":{"type":"string","format":"uuid","nullable":true},"nadjenPo":{"type":"string","nullable":true},"poruka":{"type":"string","nullable":true}}}}}}}}},"400":{"description":"The body is not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"401":{"description":"The key is missing or invalid, or the HMAC signature is invalid."},"402":{"description":"The subscription is not active or the credit is used up."},"403":{"description":"A read-only key, a register terminal key, or an IP address outside the key's list."},"413":{"description":"Body over 512 KB."},"422":{"description":"No array, it is empty or has more than 500 items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"429":{"description":"Write limit exceeded (see Retry-After)."}}}},"/api/integrations/documents":{"get":{"tags":["Reading"],"summary":"Document status by order number","description":"Same as documents/{id}, but by your externalId (and provider). Read only.","parameters":[{"name":"externalId","in":"query","required":true,"schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["woocommerce","shopify","prestashop","opencart","magento","bigcommerce","custom"],"default":"custom"}}],"responses":{"200":{"description":"Document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dokument"}}}},"400":{"description":"externalId is missing or the provider is unknown."},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"404":{"description":"It does not exist, or it is not yours."}}}},"/api/integrations/documents/{id}":{"get":{"tags":["Reading"],"summary":"Document status","description":"Same shape as the issuing response; used for checking after a 202.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dokument"}}}},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}},"404":{"description":"It does not exist, or it is not yours."}}}},"/api/integrations/tax-labels":{"get":{"tags":["Reading"],"summary":"The account's tax rates and labels","description":"For a flat-rate taxpayer all line items go as exempt (0%), whatever rate is sent. The okruzenje field says whether a request would create a real invoice: call it at startup and stop a test that targets production.","responses":{"200":{"description":"Rates, labels, note and environment of the account.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"obveznikPdv":{"type":"boolean"},"obveznikFiskalizacije":{"type":"boolean"},"okruzenje":{"type":"string","enum":["produkcija","sandbox","simulacija","nepovezano"],"description":"produkcija = real invoices at the Tax Administration; sandbox = the Tax Administration's test environment, invoices are not real; simulacija = demo account without a security element, nothing goes to the Tax Administration (the PFR number starts with MOCK); nepovezano = a production account without a security element, fiscalisation is rejected until the company enters a certificate."},"napomena":{"type":"string"},"oznake":{"type":"array","items":{"type":"object","properties":{"stopa":{"type":"string"},"oznaka":{"type":"string"},"procenat":{"type":"number"},"opis":{"type":"string"},"koristiSe":{"type":"boolean"}}}}}}}}},"402":{"description":"The subscription is not active, the credit is used up, or: The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}}}}},"/api/integrations/transactions":{"get":{"tags":["Reading"],"summary":"The company's bank transactions","description":"Incoming and outgoing payments from statements; used for matching collections. It works without an active subscription, but not without a plan that includes the API.","parameters":[{"name":"smer","in":"query","schema":{"type":"string","enum":["uplata","isplata"]}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":500,"default":200}}],"responses":{"200":{"description":"List of transactions."},"402":{"description":"The company's plan does not include the API and web shop connection (Posao or Biznis plan, the Enterprise level enabled, or a company that used the connection before 16.09.2026).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Greska"}}}}}}}},"x-webhooks":{"dogadjaji":["racun.fiskalizovan","racun.greska","refundacija.izvrsena","storno.izvrsen"],"opis":"Tezga sends a POST to the address from Settings → Integrations, with the headers X-Tezga-Event, X-Tezga-Delivery and X-Tezga-Signature (sha256 HMAC of the body). A failure is retried after 1, 5, 15, 60 and 360 minutes.","telo":{"type":"object","properties":{"dogadjaj":{"type":"string","enum":["racun.fiskalizovan","racun.greska","refundacija.izvrsena","storno.izvrsen"]},"vreme":{"type":"string","format":"date-time"},"tenantId":{"type":"string","format":"uuid"},"podaci":{"description":"For racun.* the document; for storno.izvrsen and refundacija.izvrsena the refund response (ok, status, vrsta, original, refundacija). Plus externalId and provider of your order or advance when they exist (for a cancellation or refund: of the original's order).","allOf":[{"$ref":"#/components/schemas/Dokument"},{"type":"object","properties":{"externalId":{"type":"string"},"provider":{"type":"string"}}}]}}}}}