English

FiskalAPI — Dokumentacija

REST API za izdavanje fiskalnih računa u Srbiji (e-fiskalizacija Poreske uprave). Ovo je cela površina API-ja — jedan poziv za račun, po jedan za avans i refundaciju.

Osnovno

Bazni URLhttps://api.fiskalapi.rs
FormatJSON (Content-Type: application/json)
AutentikacijaAuthorization: Bearer <API_KLJUC>

Ključevi: live i test sandbox

Dobijaš dva ključa. fapi_test_… radi u sandbox režimu — pravi tok, probni računi, ne naplaćuje se i ne troši limit. fapi_live_… izdaje prave fiskalne račune. Isti pozivi, samo zameniš ključ.

Izdavanje računa

POST /api/racuni — promet prodaja (fiskalni račun)

curl https://api.fiskalapi.rs/api/racuni \
  -H "Authorization: Bearer fapi_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentType": "Card",
    "items": [
      { "name": "Pretplata Pro/kom", "quantity": 1, "unitPrice": 2400, "labels": ["А"] }
    ],
    "kupac": { "email": "kupac@primer.rs" }
  }'

Odgovor 200

{
  "mode": "live",
  "brojRacuna": "NRXJ2JLE-XXXXXXXX-1",
  "qrKod": "data:image/png;base64,…",
  "verifikacija": "https://suf.purs.gov.rs/v/?vl=…",
  "zurnal": "…tekstualni fiskalni isečak…",
  "brojaci": { "invoiceCounter": 1, "totalCounter": 1 },
  "sdcDateTime": "2026-08-28T12:41:05+02:00"
}

sdcDateTime je vreme koje je računu dodelila Poreska uprava. Sačuvaj ga — ide kao referentDocumentDT kad taj račun budeš refundirao. Vraća se i u sandbox režimu.

Polja zahteva

PoljeOpis
items[]Stavke: name, quantity, unitPrice (RSD), labels (poreske oznake), opciono gtin. Umesto stavki može i amountRsd + description.
payments[] / paymentTypeNačin plaćanja: Cash, Card, WireTransfer, Voucher, MobileMoney, Check, Other.
labelsPoreske oznake iz GET /api/status. Npr. А = Nije u PDV (0%), Ђ = opšta (20%), Е = posebna (10%).
buyerIdOpciono — identifikacija kupca (npr. "10:PIB") za račun na firmu.
transactionIdOpciono, ali preporučeno — tvoj ID; idempotency ključ. Ponovljen poziv sa istim ID-em vraća isti račun i "ponovljen": true, ne izdaje novi.
occurredAtOpciono — vreme uplate (ЕСИР време), samo kad je novac primljen pre izdavanja računa. Šalji apsolutan trenutak u ISO 8601 sa zonom: 2026-08-04T15:20:00+02:00 ili 2026-08-04T13:20:00Z. Zapis bez zone se čita kao beogradsko vreme.

Avansni račun

POST /api/racuni/avans — za pretplate i uplate unapred. Ista struktura tela kao promet prodaja.

Slanje računa kupcu na mejl

Račun svom kupcu šalješ ti, svojim mejlom i sa svog domena: uzmi qrKod i verifikacija iz odgovora i ubaci ih u svoju potvrdu porudžbine. Polje "kupac": { "email": "…" } se podrazumevano zanemaruje (u odgovoru tada stiže napomena, a mejlPoslat je null) — mejl bi išao sa našeg domena, pa bi tuđi odbijeni mejlovi trošili reputaciju sa koje isporučujemo API ključeve i alarme.

Račun za obuku (proba kase)

POST /api/racuni/obuka — račun tipa Обука na tvom pravom bezbednosnom elementu. Prolazi ceo lanac do Poreske uprave, ali nije promet: nema poreske obaveze, ne ulazi u tvoju evidenciju prodaje i ne naplaćujemo ga. Koristi ga da proveriš da kasa radi pre prve prave kupovine. Ista struktura tela kao promet prodaja.

Refundacija

POST /api/racuni/refundacija — zahteva referentDocumentNumber (PFR broj originalnog računa) i referentDocumentDT.

Obe vrednosti stižu u odgovoru na račun koji storniraš: brojRacunareferentDocumentNumber, sdcDateTimereferentDocumentDT. Isto važi i u sandboxu, pa se refundacija može ispratiti od početka do kraja test ključem.

Delimične refundacije: refundacija sme da nosi isti transactionId kao prodaja koju storniraš — to su za nas dva različita računa. Ali druga delimična refundacija iste porudžbine mora imati drugačiji transactionId (npr. 1284-ref-1, 1284-ref-2). Ako pošalješ isti, dobićeš nazad prvu refundaciju uz "ponovljen": true — to nije greška, nego zaštita: sa iste strane ne razlikujemo „ponovi drugu ratu" od „webhook se ponovio".

Zaštita od duplog računa

Pošalji svoj transactionId uz svaki poziv. Ponovljen poziv sa istim ID-em vraća isti račun i "ponovljen": true (HTTP 200, isti oblik odgovora) — ne izdaje se drugi. To pokriva ponovljene webhookove, duple klikove i istovremene pozive.

Bez transactionId te zaštite NEMA. Svaki poziv je tada nov račun: ako procesor ponovi webhook, izdaće se drugi pravi fiskalni račun za istu kupovinu — a to se ispravlja jedino refundacijom, na teret tvoje firme. Zato u odgovoru na takav poziv stiže i napomena koja na to podseća. Polje je formalno opciono samo zbog starijih integracija; tretiraj ga kao obavezno.

Ključ je klijent + tip računa + smer + transactionId, pa isti ID sme da se koristi za promet, avans i obuku — to su različiti računi i ne poništavaju se međusobno.

Čitanje izdatog računa

GET /api/racuni/{id} — vraća već izdat račun, po tvom transactionId ili po PFR broju računa. Za slučaj da izgubiš odgovor sa izdavanja: dokument je već prijavljen Poreskoj upravi i mora da postoji način da se pročita nazad.

Vraćaju se brojRacuna, iznos, sdcDateTime i verifikacija, a qrKod, zurnal i brojaci dok god je zapis o izdavanju kod nas (35 dana). Ako isti transactionId nosi više dokumenata (prodaja pa refundacija), racun je najnoviji, a ceo lanac stoji u racuni.

Vide se samo tvoji i samo pravi (live) računi — test računi se ne pamte. Nepoznat ID vraća 404. U sandboxu je zato odgovor na POST jedini izvor — sadrži sva polja, sdcDateTime uključujući.

Status i stope

GET /api/status — podaci obveznika, ESIR i aktuelne poreske stope (oznake i procenti). Uvek povlači stope od Poreske uprave — nikad ih ne kucaj ručno.

Potrošnja

GET /api/usage — broj računa, mesečni trošak i iskorišćenost limita za tekući mesec (opciono ?month=YYYY-MM).

Kodovi grešaka

KodZnačenje
401Nevalidan API ključ.
403Nalog nije aktivan ili je suspendovan (neizmirena obaveza).
402Dostignut mesečni limit računa.
400Neispravno telo zahteva (npr. zbir plaćanja ≠ iznos, nepoznata oznaka).
409Prethodni pokušaj sa istim transactionId nije razrešen — račun je možda već izdat, pa drugi ne izdajemo. Javi nam transactionId; proveravamo kod Poreske uprave.
502Greška prema V-PFR-u Poreske uprave. Ako odgovor nosi "statusRacuna": "nepoznato", veza je pukla posle slanja i račun je možda izdat — ne ponavljaj automatski.

Kad veza pukne usred izdavanja

Ako zahtev ode Poreskoj upravi, a odgovor ne stigne (timeout, prekid veze), niko na našoj strani ne zna da li je račun izdat. Tada dobijaš 502 sa "statusRacuna": "nepoznato".

Automatsko ponavljanje bi u tom slučaju moglo da napravi drugi pravi fiskalni račun za istu kupovinu — greška koja se ispravlja jedino refundacijom. Zato ponovljen poziv sa istim transactionId vraća 409 dok se ne proveri šta se stvarno desilo. Ako se račun u međuvremenu nađe u evidenciji, sve se razrešava samo i dobijaš ga nazad sa "ponovljen": true.

Izmene API-ja

DatumŠta se promenilo
2026-08-28 Sandbox vraća sdcDateTime i brojaci — odgovor sa test ključem sada ima ista polja kao live. Refundacija traži referentDocumentDT, a test računi se ne pamte, pa se to vreme uzima iz odgovora na sam POST.
2026-08-22 Novo: GET /api/racuni/{id} — izdat račun se sada može pročitati nazad, po transactionId ili po PFR broju. Ranije je odgovor na POST bio jedini primerak.
Ispravljeno u ovoj dokumentaciji: kupac.email se podrazumevano zanemaruje — mejl kupcu šalje prodavac. Ranije je ovde pisalo da račun „odmah stiže kupcu na mejl".
2026-08-09 occurredAt se čita kao apsolutan trenutak. Ranije se vrednost prosleđivala Poreskoj upravi doslovno, a V-PFR cifre štampa neizmenjene — pa je vreme na računu izlazilo pomereno za pomak zone, i integracije koje su to primetile slale su beogradski sat sa oznakom Z da bi ispis bio tačan.
Od ovog datuma vreme prevodimo mi: pošalji stvaran trenutak (2026-08-04T13:27:37Z ili 2026-08-04T15:27:37+02:00) i na računu piše 15:27:37. Zapis bez zone se čita kao beogradsko vreme.
Ako je tvoja integracija ranije dodavala sat/dva da bi ispis bio tačan — ukloni to, inače će račun sada nositi vreme pomereno unapred. Prepoznajemo taj obrazac i upozorenje ti stoji u „Moj nalog".
2026-08-09 Zaštita od duplog računa i kod 409. Ponovljen transactionId vraća isti račun umesto novog; prekid veze posle slanja daje 502 sa "statusRacuna": "nepoznato", a naredni pokušaj 409 dok se ne razreši.
2026-08-09 Vremena u prikazima i obračunu su beogradska. Dan i mesec se seku na beogradskoj ponoći — račun izdat 1. u 00:30 pripada novom mesecu (fakture, KPO knjiga).

Pitanja? Kontakt · Početna · English version