SEF API integracija: kako automatizovati slanje faktura

Ako izdaješ desetine faktura mesečno, ručno prekucavanje svake fakture na SEF portalu brzo postaje najsporiji deo posla. SEF ima javni API koji ti to rešava: iz svog softvera direktno šalješ fakturu, pratiš status i preuzimaš ulazne fakture, bez otvaranja portala. U nastavku je pregled kako ta integracija stvarno izgleda — okruženja, autentifikacija, format fakture, slanje, praćenje statusa i najčešće greške.
SEF, ne poseban registar sa sopstvenom integracijom
SEF (Sistem elektronskih faktura) je sistem koji vidiš kao korisnik — portal, Inbox, statusi. CRF (Centralni registar faktura) je poseban registar Uprave za trezor za fakture prema javnom sektoru; kad fakturu takvom kupcu pošalješ preko SEF-a, SEF je prosleđuje u CRF (u API pozivu to je parametar sendToCir), pa CRF ne integrišeš posebno. Kad automatizuješ slanje, integrišeš se na javni SEF API — iste endpointe koje koristi i sam portal.
Test i produkciono okruženje
SEF razdvaja test i produkciono okruženje, svako sa sopstvenim API ključem:
| Okruženje | Portal | Namena |
|---|---|---|
| Produkcija | efaktura.mfin.gov.rs | slanje pravih faktura |
| Demo (test) | demoefaktura.mfin.gov.rs | razvoj i testiranje integracije, bez posledica na pravi promet |
Ključ napravljen na jednom okruženju ne radi na drugom — integraciju uvek prvo testiraš na demo okruženju, pa tek onda prebacuješ na produkciju.
Autentifikacija: API ključ sa SEF portala
Prijava na sam SEF portal ide preko eID naloga visokog nivoa pouzdanosti (kvalifikovani elektronski sertifikat ili aplikacija ConsentID). Za API integraciju ti to ne treba pri svakom pozivu — na portalu, u Podešavanja → API menadžment, napraviš API ključ i njega šalješ u zaglavlju svakog HTTP poziva:
curl -X POST "https://efaktura.mfin.gov.rs/api/publicApi/sales-invoice/ids?dateFrom=2026-09-01&dateTo=2026-09-28" \
-H "ApiKey: $SEF_API_KEY"
Nema posebnog koraka razmene tokena — ključ važi dok ga ručno ne povučeš ili zameniš na portalu.
Struktura fakture: UBL 2.1 XML
SEF prihvata fakturu isključivo kao UBL 2.1 XML. Skelet minimalne fakture izgleda ovako (vrednosti su ilustrativne):
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:mfin.gov.rs:srbdt:2022</cbc:CustomizationID>
<cbc:ID>[broj-fakture]</cbc:ID>
<cbc:IssueDate>2026-09-28</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>RSD</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="9948">[PIB izdavaoca]</cbc:EndpointID>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="9948">[PIB kupca]</cbc:EndpointID>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<cbc:ID>[broj računa]</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:TaxTotal>
<cac:TaxSubtotal>
<cac:TaxCategory>
<cbc:ID>SS</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cbc:TaxExemptionReasonCode>PDV-RS-33</cbc:TaxExemptionReasonCode>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:InvoiceLine>...</cac:InvoiceLine>
</Invoice>
schemeID="9948" je standardna ISO 6523 oznaka za PIB. InvoiceTypeCode 380 označava običnu fakturu. Primer koristi poresku kategoriju SS sa osnovom PDV-RS-33 — kategorija za paušalce i male obveznike van sistema PDV-a (član 33. Zakona o PDV-u); PDV obveznik na standardnoj stopi umesto toga koristi kategoriju S sa stvarnim procentom. PaymentMeansCode 30 označava plaćanje transferom; broj računa upisuješ u PayeeFinancialAccount.
Slanje fakture na SEF
Kad je XML gotov, fakturu šalješ kao fajl, ne kao XML telo zahteva — poziv je multipart upload sa jedinstvenim requestId po pokušaju:
curl -X POST "https://efaktura.mfin.gov.rs/api/publicApi/sales-invoice/ubl/upload?sendToCir=No&requestId=[requestId]" \
-H "ApiKey: $SEF_API_KEY" \
-F "ublFile=@faktura.xml;type=text/xml"
Parametar sendToCir ide uz upload i zavisi od tipa primaoca — pogrešna vrednost vraća grešku UBLReceiverCannotSendToCir, pa proveri kod dokumentacije ili dobavljača softvera koju vrednost tvoj slučaj traži. Uspešan odgovor vraća HTTP 200 i SalesInvoiceId fakture na SEF-u — to je potvrda da je faktura primljena.
Svaki pokušaj slanja nosi nov requestId, generisan pre poziva. Ako poziv istekne ili SEF vrati grešku na svom serveru (5xx), ne šalji isti fajl ponovo naslepo — prvo proveri da li je faktura ipak stigla, preko liste poslatih faktura (sales-invoice/ids) za taj datum. Ako je stigla, nastavljaš dalje; ako nije, tek onda šalješ ponovo sa novim requestId.
Praćenje statusa poslatih faktura
SEF ne šalje sam obaveštenje kad se status promeni — tvoj sistem periodično pita SEF za status. Za izlaznu fakturu to ide preko poziva ka sales-invoice sa ID-jem fakture; vraća trenutni status i, ako je odbijena, razlog. Za listu svih poslatih faktura u periodu koristiš sales-invoice/ids sa opsegom datuma i onda povlačiš status za svaku.
U praksi to znači periodičnu sinhronizaciju — na primer, svakih nekoliko minuta — koja povuče listu promena i ažurira status u tvojoj bazi. Nema mehanizma kojim SEF sam gurne promenu ka tebi; provera je uvek na tvojoj strani.
Prijem ulaznih faktura
Za ulazne fakture (koje tebi šalju drugi) princip je isti: prvo povučeš listu ID-jeva promenjenih faktura preko purchase-invoice/ids za dati period, pa za svaku po potrebi preuzmeš pun XML preko purchase-invoice/xml. Nova ulazna faktura ima status koji ide iz "nova" u "pregledana" kad je otvoriš, a onda je prihvataš ili odbijaš pozivom koji uz odluku nosi i komentar — koristan kad odbijaš fakturu i primalac treba da zna zašto.
Greške i kako se oporaviti
Najčešće greške pri pozivima:
| Kod | Značenje | Šta uraditi |
|---|---|---|
| 400 | Nevalidan XML ili nepotpuna faktura | Proveri poruku greške, ispravi fajl, pošalji ponovo sa novim requestId |
| 401 | API ključ nije validan ili je povučen | Proveri ključ u podešavanjima, generiši novi ako treba |
| 429 | Previše zahteva u kratkom roku (SEF ograničava broj poziva — tačnu granicu proveri u aktuelnoj API dokumentaciji) | Sačekaj i pošalji ponovo, ne šalji pozive unapred u seriji |
| 5xx | Greška na strani SEF-a | Proveri da li je faktura ipak stigla pre nego što šalješ ponovo |
Za 429 i za probleme sa konekcijom ima smisla automatski pokušati ponovo, uz pauzu koja raste:
import time
def send_invoice_with_retry(send_fn, max_retries=5):
for attempt in range(max_retries):
try:
response = send_fn()
if response.status_code == 200:
return response
if response.status_code == 429:
time.sleep(2 ** attempt)
continue
raise Exception(f"SEF greska: {response.status_code}")
except ConnectionError:
time.sleep(2 ** attempt)
raise Exception("Slanje nije uspelo posle vise pokusaja")
Za 400 i 401 automatski retry nema smisla — to su greške koje ponovni identičan poziv neće rešiti; treba ih prikazati i ispraviti ručno.
Povezivanje ERP softvera sa SEF-om
Ako već koristiš gotov softver za fakturisanje ili ERP — npr. Pantheon ili Minimax i slični — proveri kod dobavljača da li već ima ugrađenu SEF integraciju pre nego što praviš sopstvenu. Provera se svodi na nekoliko pitanja:
- Da li softver ima poseban modul za e-fakture ili SEF?
- Da li tvoj SEF API ključ već možeš da uneseš u podešavanja softvera?
- Da li softver ume da generiše UBL 2.1 XML sa ispravnom poreskom kategorijom za tvoj slučaj (S, SS, AE, Z, E, R ili O)?
Ako pišeš sopstvenu integraciju, prvi koraci uvek idu na demo okruženju — ceo tok slanja i praćenja statusa testiraš tamo, pre nego što dotakneš produkcioni ključ.
Gde Fakturko ulazi u priču
Ako ne želiš da pišeš i održavaš ovu integraciju sam, Fakturko ima ugrađenu vezu sa SEF-om. Fakturu koju napraviš u aplikaciji šalješ na SEF direktno iz nje, ili preko Faktora na Telegramu ili WhatsApp-u — Faktor pripremi fakturu, a ti potvrdiš slanje. Status svake poslate fakture vidiš u aplikaciji; ulazne fakture stižu u Inbox, gde ih prihvataš ili odbijaš pojedinačno ili više odjednom. Taj SEF API ključ upišeš u Fakturko podešavanja na jednom mestu — aplikacija ga dalje čuva i koristi za sva dalja slanja i proveru statusa.
Zaključak
Tačne endpointe, granične vrednosti i format grešaka SEF menja i dopunjuje s vremena na vreme — pre nego što gradiš integraciju, proveri aktuelnu tehničku dokumentaciju na efaktura.gov.rs. Ako želiš da probaš slanje bez pisanja koda, Fakturko nudi 90 dana probnog perioda sa punim Pro paketom, bez kartice.
Česta pitanja
Kako da automatizujem slanje faktura na SEF bez ručnog unosa?
SEF ima javni API koji ti to omogućava: fakturu pripremiš kao UBL 2.1 XML i pošalješ je programski na SEF, umesto da je ručno prekucavaš na portalu. Za to ti treba API ključ sa SEF portala (Podešavanja → API menadžment) koji šalješ u zaglavlju ApiKey uz svaki poziv. Ako ne želiš da gradiš integraciju sam, alati poput Fakturka već imaju ugrađenu vezu sa SEF-om i fakturu šalju direktno iz aplikacije.
Koja mi autentifikacija treba za SEF API?
Za pozive ka SEF API-ju treba ti API ključ koji generišeš na SEF portalu, u Podešavanja → API menadžment, odvojeno za demo i za produkciono okruženje. Ključ šalješ u zaglavlju ApiKey uz svaki HTTP poziv — nema dodatnog koraka razmene tokena. Prijava na sam portal, radi generisanja ključa, ide preko eID naloga (kvalifikovani sertifikat ili ConsentID).
Koji format fakture prihvata SEF API i šta mora da sadrži?
SEF prihvata fakturu kao UBL 2.1 XML. Obavezno sadrži broj i datum fakture, tip dokumenta, valutu, podatke izdavaoca i kupca sa PIB-om (u polju EndpointID sa oznakom 9948), poresku kategoriju po stavci (na primer S za standardnu stopu ili SS sa osnovom PDV-RS-33 za paušalce van PDV-a), i stavke fakture sa količinom, cenom i iznosom. Faktura se šalje kao fajl u multipart pozivu, ne kao XML telo zahteva.
Koja je razlika između demo i produkcionog okruženja za SEF API?
Demo okruženje (demoefaktura.mfin.gov.rs) služi za razvoj i testiranje integracije — fakture poslate tamo ne idu u pravi promet i ne utiču na tvoje obaveze. Produkcija (efaktura.mfin.gov.rs) je za prave fakture. Svako okruženje ima svoj API ključ; ključ iz jednog okruženja ne radi u drugom, pa integraciju uvek prvo testiraš na demo pre nego što je prebaciš na produkciju.
Da li je SEF obavezan za moje preduzeće?
Sistem e-faktura (SEF) je obavezan za PDV obveznike u Srbiji — od 1. januara 2023. fakture drugim PDV obveznicima izdaju se kroz SEF, a kroz SEF moraju i svi koji fakturišu javnom sektoru, uključujući paušalce van PDV-a. Ručni unos preko besplatnog državnog portala je sasvim legalan, ali kod većeg broja faktura oduzima mnogo vremena. API integracija automatizuje taj proces i najskalabilnija je opcija za firme sa većim brojem faktura.