CRF integracija: kako automatizovati slanje faktura preko

Automatizacija faktura na laptopu sa dokumentima na stolu

Ručno ubacivanje svake fakture u SEF portal Ministarstva finansija je 15-ak minuta po fakturi — i to samo ako sve prođe iz prvog pokušaja. Na 200 faktura mesečno, to je 50 sati gubitnog vremena.

Centralni registar faktura (CRF) — tehnička infrastruktura koja stoji iza SEF-a — ima REST API koji omogućava automatsku registraciju faktura direktno iz tvog softvera. Umesto ručnog unosa na portalu, faktura se šalje programski, dobija UUID i status, i sve se loguje bez ljudskog dodira. Evo kako taj API funkcioniše, šta mora da sadrži payload, i gde uobičajeno puca integracija.

Šta je CRF i kako radi tehnički

Centralni registar faktura (CRF) je sistem koji prihvata, validira i razmenjuje e-fakture između izdavaoca i primaoca u Srbiji. Nije poseban portal — to je backend infrastruktura iza SEF-a (Sistema e-faktura), koji je obavezan za sve obveznike PDV-a od 1. januara 2023. godine, a od 2025. i za veliki broj ne-PDV obveznika.

CRF radi na principu validacije i raspodele: izdavalac pošalje fakturu u standardizovanom XML/JSON formatu, CRF je validira protiv šeme (syntax + semantika), dodeli joj jedinstveni identifikator (UUID), i prosledi primaocu u njegov Inbox. primalac može da prihvati, odbije, ili stavi u status "ispituje se".

Tehnički gledano, CRF API je RESTful servis koji koristi TLS 1.2+ šifrovanje, autentifikuje pozive preko sertifikata ili OAuth2 tokena, i vraća JSON odgovore sa statusnim kodovima. Komunikacija se odvija preko dva okruženja:

Okruženje URL baza Namena
Test (sandbox) https://api.demo.e-faktura.gov.rs Razvoj i testiranje integracije
Produkcija https://api.e-faktura.gov.rs Slanje pravih faktura

Sve ide preko HTTPS-a. Nema HTTP fallback-a — neštifikovani pozivi se odbijaju na nivou load balancera.

Autentifikacija: sertifikat vs. OAuth2 token

CRF API nudi dva načina autentifikacije, i izbor zavisi od tipa integracije koju praviš.

1. Kvalifikovani elektronski sertifikat (QES) — koristi se za slanje faktura u ime pravnog lica. Sertifikat izdaje Ministarstvo finansija ili akreditovani pružalac sertifikacionih usluga (npr. Posta Srbije, Halcom). Sertifikat se instalira na server i referencira preko SSL_CERT_FILE okruženja ili se prosleđuje kao deo HTTPS zahteva.

curl -X POST https://api.e-faktura.gov.rs/api/sales-invoice \
  --cert client.crt \
  --key client.key \
  -H "Content-Type: application/xml" \
  -d @invoice.xml

2. OAuth2 bearer token — koristi se za pristup Inbox-u (prijem ulaznih faktura) i za upite statusa. Token se dobija razmenom sertifikata za JWT token, koji važi ograničeno vreme (proveri aktuelni TTL u službenoj dokumentaciji).

Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI...

U praksi, većina integracija koristi kombinaciju: sertifikat za slanje faktura, OAuth2 token za čitanje Inbox-a i proveru statusa.

Gde najčešće puca

Najčešći problem u ovoj fazi nije sam kod — to je validacija sertifikata. Ako sertifikat istekne, svi pozivi padaju sa 401 Unauthorized. Praktičan savet: postavi monitoring na datum isteka sertifikata najmanje 30 dana unapred, jer obnova u Srbiji često ide preko fizičkog odlaska u poštu ili banku.

Payload struktura: šta CRF očekuje

CRF prihvata fakture u formatu definisanom UBL 2.1 (Universal Business Language) standardom, prilagođenom srpskom zakonodavstvu. JSON i XML formati su podržani, ali XML je dominantan u praksi jer ga proizvode svi veći ERP sistemi.

Minimalni payload koji CRF prihvata sadrži sledeće obavezne elemente:

Element Opis Primer
cbc:ID Broj fakture 2026-0014
cbc:IssueDate Datum izdavanja 2026-08-02
cbc:DueDate Datum valute 2026-08-17
cac:AccountingSupplierParty Podaci izdavaoca (PIB, MB, adresa) PIB: 123456789
cac:AccountingCustomerParty Podaci kupca (PIB, MB, adresa) PIB: 987654321
cbc:TaxPointDate Datum poreske obaveze 2026-08-02
cac:TaxTotal Ukupan PDV po stopama 20%: 2,000.00 RSD
cbc:LegalMonetaryTotal Ukupni iznosi (pre PDV, PDV, ukupno) 12,000.00 RSD
cac:InvoiceLine Stavke fakture (opis, količina, cena, poreska stopa) 1× Konsultacije = 10,000 RSD

Evo primera minimalnog JSON payloada:

{
  "ID": "2026-0014",
  "IssueDate": "2026-08-02",
  "DueDate": "2026-08-17",
  "AccountingSupplierParty": {
    "Party": {
      "PartyIdentification": {
        "ID": {"schemeID": "9948", "value": "123456789"}
      },
      "PartyName": {"Name": "Moja Firma DOO"}
    }
  },
  "AccountingCustomerParty": {
    "Party": {
      "PartyIdentification": {
        "ID": {"schemeID": "9948", "value": "987654321"}
      },
      "PartyName": {"Name": "Klijent DOO"}
    }
  },
  "TaxPointDate": "2026-08-02",
  "TaxTotal": [{
    "TaxAmount": {"currencyID": "RSD", "value": "2000.00"},
    "TaxSubtotal": [{
      "TaxableAmount": {"currencyID": "RSD", "value": "10000.00"},
      "TaxCategory": {"Percent": "20"}
    }]
  }],
  "LegalMonetaryTotal": {
    "TaxExclusiveAmount": {"currencyID": "RSD", "value": "10000.00"},
    "TaxInclusiveAmount": {"currencyID": "RSD", "value": "12000.00"},
    "PayableAmount": {"currencyID": "RSD", "value": "12000.00"}
  },
  "InvoiceLine": [{
    "ID": "1",
    "InvoicedQuantity": {"unitCode": "HUR", "value": "10"},
    "LineExtensionAmount": {"currencyID": "RSD", "value": "10000.00"},
    "Item": {"Name": "Konsultacije"},
    "Price": {"PriceAmount": {"currencyID": "RSD", "value": "1000.00"}}
  }]
}

Šema 9948 u schemeID označava PIB (poreski identifikacioni broj) po ISO 6523 standardu. Ovo je obavezno — bez toga CRF ne može da identifikuje strane.

Polja koja najčešće fale

U praksi, ova tri polja uzrokuju najviše odbijenih faktura:

  1. TaxPointDate — datum nastanka poreske obaveze. Često se meša sa IssueDate. Ako je faktura izdata 2. avgusta, a usluga pružena 15. jula, TaxPointDate je 15. jul (za PDV obveznike koji knjige po toku novca ili po toku robe/usluge — zavisi od metoda).

  2. cbc:TaxCurrencyCode — ako fakturišeš u stranoj valuti (EUR), poreski deo mora biti izražen u dinarima po kursu NBS na dan poreske obaveze. Ovo polje mora biti RSD čak i kad je DocumentCurrencyCode EUR.

  3. cac:PaymentMeans — način plaćanja i bankovni račun. CRF ga ne zahteva kao obavezno za prihvatanje, ali primalac ne može da plati fakturu bez njega, a neki ERP sistemi ga traže za automatsku obradu.

Slanje fakture: HTTP poziv i odgovor

Jednom kad imaš validan payload, slanje ide preko POST zahteva na /api/sales-invoice endpoint.

POST /api/sales-invoice HTTP/1.1
Host: api.e-faktura.gov.rs
Content-Type: application/xml
Accept: application/json

<Invoice xmlns="...">
  <!-- payload -->
</Invoice>

Uspesan odgovor (HTTP 201) sadrži UUID i status:

{
  "invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "DELIVERED",
  "receivedAt": "2026-08-02T14:32:15.000Z"
}

Mogući statusi nakon slanja:

Status Značenje Šta dalje
ACCEPTED CRF je validirao i prosledio primaocu Ništa — čekaš akciju primaoca
DELIVERED Primalac je primio u Inbox Prati status — može prihvatiti/odbiti
REJECTED Validacija pala Pročitaj errorMessage, ispravi, šalji ponovo
PROCESSING CRF obrađuje Poll-uj status nakon 30 sekundi

Idempotencija: Ako pošalješ istu fakturu dva puta (npr. zbog timeout-a na tvom kraju), CRF će prepoznati duplikat na osnovu broja fakture + PIB-a izdavaoca i vratiti postojeći UUID umesto novog. Ovo je korisno jer možeš bezbedno da radiš retry bez straha od duplih faktura.

Error handling: šta znače kodovi i kako se oporavlja

CRF API koristi standardne HTTP statusne kodove, ali sa dodatnim greškama u telu odgovora koje su specifične za domensku logiku.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Tax point date cannot be after issue date",
    "field": "cbc:TaxPointDate",
    "line": 14
  }
}

Najčešće greške u praksi:

Kod Uzrok Rešenje
400 BAD_REQUEST Nevalidan XML/JSON, nepotpuna šema Parsiraj grešku, proveri liniju iz poruke
401 UNAUTHORIZED Sertifikat nije prošao ili je istekao Obnovi sertifikat, proveri lanac poverenja
409 CONFLICT Faktura sa istim brojem već postoji Nemoj slati ponovo — preuzmi postojeći UUID
422 UNPROCESSABLE_ENTITY Sintaksa je dobra, ali domenska logika pada Npr. PIB ne postoji u registru, negativan iznos, pogrešan datum
429 TOO_MANY_REQUESTS Premašio rate limit Backoff strategija, povećaj interval između poziva
500 INTERNAL_SERVER_ERROR Greška na strani CRF-a Retry sa eksponencijalnim backoff-om (3, 9, 27 sekundi)

Retry strategija koja radi

Za 5xx greške, koristi eksponencijalni backoff sa jitter-om:

import time
import random

def send_invoice_with_retry(payload, max_retries=5):
    for attempt in range(max_retries):
        try:
            response = post_to_crf(payload)
            if response.status_code in (200, 201):
                return response.json()
            elif response.status_code == 429:
                wait = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(wait)
            elif response.status_code >= 500:
                wait = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(wait)
            else:
                raise Exception(f"Non-retryable: {response.status_code}")
        except ConnectionError:
            wait = (2 ** attempt) + random.uniform(0, 1)
            time.sleep(wait)
    raise Exception("Max retries exceeded")

Za 4xx greške ne radi retry — to su domenske greške koje se neće rešiti ponovnim slanjem. Treba ih logovati, prikazati korisniku, i zahtevati ljudsku intervenciju.

Webhook povratni pozivi: status primaoca u realnom vremenu

Pošto faktura stigne u Inbox primaoca, CRF ne garantuje da će primalac odmah reagovati — on može prihvatiti fakturu za 5 minuta ili za 5 dana. Da ne bi morao da poll-uješ status svakih sat vremena, CRF podržava webhook povratne pozive.

Registracija webhook-a ide preko POST /api/webhook-registration:

{
  "callbackUrl": "https://moj-sistem.rs/api/crf-webhook",
  "events": ["INVOICE_ACCEPTED", "INVOICE_REJECTED", "INVOICE_DELIVERED"]
}

Kada se desi neki od događaja, CRF šalje POST na tvoj URL:

{
  "event": "INVOICE_ACCEPTED",
  "invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "acceptedBy": {"pib": "987654321", "name": "Klijent DOO"},
  "acceptedAt": "2026-08-02T15:45:00.000Z"
}

Bezbednost webhook-a

CRF šalje X-Signature header sa HMAC-SHA256 potpisom tela. Tvoj endpoint mora da verifikuje taj potpis — drugačije si otvoren za lažne zahteve:

import hmac
import hashlib

def verify_webhook(request_body, signature_header, secret):
    expected = hmac.new(
        secret.encode(),
        request_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

Ako potpis ne prolazi, vrati 401 i ignoriši zahtev. Nema izuzetaka.

Šta radi sa statusom

Kada webhook stigne sa INVOICE_ACCEPTED, tvoj sistem treba da:

  1. Ažurira status fakture u lokalnoj bazi na accepted
  2. Pošalje potvrdu korisniku (email, notifikacija u aplikaciji)
  3. Ako je faktura deo recurring serije, zabeleži status za buduće referentne fakture

Za INVOICE_REJECTED, obavezno sačuvaj i razlog odbijenja — korisnik mora znati zašto je klijent odbio fakturu, jer to čestto znači da treba da se izda nova (ispravljena) ili da se razreši spor sa klijentom direktno.

Povezivanje popularnih ERP sistema sa CRF-om

Većina preduzetnika u Srbiji ne piše custom integraciju — oni već koriste neki softver i pitaju se kako da ga povežu sa SEF-om. Evo kako to izgleda u tri najčešća scenarija.

Scenario 1: Slobodan preduzetnik sa Excelom

Najteži slučaj. Excel nije ERP, nema API, i ne može direktno da šalje ka CRF-u. Dve opcije:

  • Manuelni upload na SEF portalu — uploaduješ XML fajl koji generišeš iz Excel-a (kroz neki makro ili dodatak). Radi, ali je 50% ručnog posla još uvek tu.
  • Migracija na softver koji ima integraciju — prelazak na Fakturko ili sličan alat koji ima native SEF integraciju. Tu više nema Excel-a ni XML fajlova.

Ako si paušalac sa 10-15 faktura mesečno, manuelni upload je sasvim dovoljan. Za sve iznad toga, vreme provedeno na portalu postaje bolan trošak.

Scenario 2: Mali DOO sa postojećim ERP-om

Ako već koristiš softver poput Pantheon, Calculus, Datalab (Slnik), ili Minimax — svi oni imaju ugrađenu SEF/CRF integraciju od 2023/2024. godine. Pitanje je samo da li je aktivirana i konfigurisana.

Proveri u svom softveru:

  1. Da li postoji modul za "e-fakture" ili "SEF integraciju"
  2. Da li je sertifikat instaliran na serveru ili računaru gde softver radi
  3. Da li su PIB i matični broj firme uneti u podešavanjima

Ako je sve na mestu, slanje ide direktno iz softvera — jedna faktura, jedan klik na "Pošalji na SEF". Neki od ovih sistema već imaju i webhook podršku za povratne statuse.

Scenario 3: Srednja firma sa custom softverom

Ako imaš custom ERP ili interni sistem za fakturisanje, integracija je projekat — ne jedno popodne posla. Konkretni koraci:

  1. Analiza postojeće šeme faktura — kako tvoj sistem čuva podatke o fakturi i da li su sva obavezna polja prisutna
  2. Mapiranje na UBL 2.1 — kreiranje transformacije iz tvoje šeme u CRF format
  3. Implementacija API klijenta — autentifikacija, slanje, retry logika, error handling
  4. Webhook endpoint — prijem i obrada statusnih promena
  5. Testiranje na sandbox-u — Ministarstvo nudi demo okruženje gde možeš slati test fakture bez posledica
  6. Produkcioni deploy — prelazak na pravi API sa monitoringom

Realan vremenski okvir za iskusnog developera: 5-10 radnih dana za osnovnu integraciju, plus 3-5 dana za edge cases i stabilizaciju.

Gde Fakturko ulazi u priču

Ako ne želiš da pišeš niti liniju koda, niti da se boriš sa sertifikatima i XML šemama — Fakturko ima ugrađenu, zvaničnu REST API integraciju sa Ministarstvom finansija. Faktura koju kreiraš u aplikaciji se automatski šalje na SEF, dobija UUID, i ti u realnom vremenu vidiš da li je klijent prihvatio ili odbio. Sve radi u pozadini — ti samo popuniš fakturu ili kažeš Faktoru preko Telegrama "pošalji fakturu Žiki za 50.000 dinara", i gotovo.

Na Pro planu, to uključuje i batch slanje (više faktura odjednom), automatski prijem ulaznih faktura u Inbox, i webhook notifikacije koje ti javljaju čim se bilo šta desi sa bilo kojom fakturom. Ako već imaš ERP koji nema SEF integraciju, Fakturko može da preuzme taj deo toka — ti i dalje vodiš knjigovodstvo u svom sistemu, a Fakturko brine o CRF komunikaciji.


CRF integracija nije raketna nauka, ali je sitan, detaljan posao koji pojede vreme ako ga radiš ručno ili poluautomatski. Sertifikati ističu, XML validacija pada na trivijalnim stvarima, a webhook-ovi se gube u mreži u najgorem mogućem trenutku. Ako ti fakturisanje nije core biznis — a za 99% preduzetnika u Srbiji nije — probaj Fakturko besplatno i vidi koliko ti vremena vraća svakog meseca.

Česta pitanja

Kako da automatizujem slanje faktura na SEF portal bez ručnog unosa?

Centralni registar faktura (CRF) nudi RESTful API koji omogućava automatsku registraciju faktura direktno iz tvog softvera, umesto ručnog unosa na portalu. API radi preko HTTPS protokola i zahteva autentifikaciju pomoću kvalifikovanog elektronskog sertifikata ili OAuth2 tokena. Faktura se šalje u standardizovanom XML ili JSON formatu po UBL 2.1 standardu, nakon čega sistem vraća jedinstveni identifikator (UUID) i status fakture. Na ovaj način se kompletno eliminiše ljudski rad i uštedi na desetine sati mesečno.

Koja mi autentifikacija treba za CRF API integraciju?

CRF API podržava dva načina autentifikacije: kvalifikovani elektronski sertifikat (QES) i OAuth2 bearer token. Sertifikat se koristi za slanje faktura u ime pravnog lica, instalira se na server i prosleđuje kao deo HTTPS zahteva, a izdaje ga Ministarstvo finansija ili akreditovani pružalac usluga. OAuth2 token se koristi za pristup Inboxu i proveru statusa faktura, a dobija se razmenom sertifikata za JWT token. U praksi većina integracija koristi kombinaciju oba metoda. Ključno je postaviti monitoring isteka sertifikata bar 30 dana unapred jer obnova u Srbiji zahteva fizički odlazak u poštu ili banku.

Koji format fakture prihvata SEF API i šta mora da sadrži?

CRF API prihvata fakture u XML ili JSON formatu zasnovanom na UBL 2.1 (Universal Business Language) standardu, prilagođenom srpskom zakonodavstvu. Obavezni elementi uključuju broj fakture, datum izdavanja, datum valute, PIB i matični broj izdavaoca i kupca, datum poreske obaveze, ukupan PDV po stopama i stavke fakture. PIB se navodi po ISO 6523 standardu sa oznakom schemeID 9948. Najčešće polje koje fali ili se pogrešno unosi je TaxPointDate (datum poreske obaveze), koji ljudi mešaju sa datumom izdavanja fakture.

Koja je razlika između test i produkcija okruženja za SEF API?

CRF API ima dva odvojena okruženja: test (sandbox) na adresi api.demo.e-faktura.gov.rs za razvoj i testiranje integracije, i produkciju na api.e-faktura.gov.rs za slanje pravih faktura. Oba okruženja koriste isključivo HTTPS komunikaciju sa TLS 1.2+ šifrovanjem, bez HTTP fallback opcije. Pre prelaska na produkciju preporučuje se temeljno testiranje na sandbox okruženju. Sva komunikacija se odvija preko RESTful servisa koji vraća JSON odgovore sa standardnim HTTP statusnim kodovima.

Da li je SEF integracija obavezna za moje preduzeće?

Sistem e-faktura (SEF) je obavezan za sve PDV obveznike u Srbiji od 1. januara 2023. godine, a od 2025. obaveza se proširuje i na veliki broj ne-PDV obveznika. Iako se ručni unos fakura preko portala funkcionalno može koristiti, on zahteva oko 15 minuta po fakturi, što kod 200 faktura mesečno znači 50 sati izgubljenog vremena. API integracija omogućava potpunu automatizaciju ovog procesa i jedina je skalabilna opcija za firme sa većim brojem faktura. Centralni registar faktura (CRF) je tehnička infrastruktura koja omogućava ovu integraciju.