API za programere 4invoices

REST preko HTTPS-a, JSON, javna OpenAPI specifikacija. Fakturisanje, klijenti, serije i cijeli ciklus nacionalnog e-fakturisanja (ANAF e-Factura u Rumuniji) — iz vašeg ERP-a, prodavnice ili CRM-a.

Javni API 4invoices šalje podatke o fakturisanju iz vašeg sistema u 4invoices i vodi fakturu sve do nacionalnog sistema e-fakturisanja (u Rumuniji: ANAF SPV). Jedan produkcijski host, jedan client_credentials tok, idempotentni fiskalni zapisi i jedan omotač grešaka na svim rutama.

https://api.4invoices.net/v1 · OpenAPI specifikacija (ključ nije potreban)

Šta je API 4invoices

  • Javni API 4invoices je REST preko HTTPS-a, s JSON-om na ulazu i izlazu.
  • Jedan produkcijski host: https://api.4invoices.net/v1. Ne postoji poseban sandbox host.
  • OpenAPI 3.1 specifikacija je javna i bez ključa — uvezite je direktno u Postman ili u generator klijenata.
  • Svaki odgovor nosi zaglavlje X-Request-Id; navedite ga kada tražite podršku.
  • Svaki fiskalni zapis je idempotentan: ponovni pokušaj nakon isteka vremena nikada ne izdaje drugi dokument.
Samo server-server. API nikada ne pozivajte iz JavaScript-a u pregledaču: client_secret bi dospio do posjetioca, a CORS i inače blokira sve izvore.
  • Cijeli ciklus nacionalnog e-fakturisanja preko API-ja (u Rumuniji: ANAF e-Factura): prethodna provjera, podnošenje, praćenje, potpisani odgovor i ulazno sanduče. Nijedan rumunski konkurent ovo ne izlaže.
  • SmartBill nema nijednu krajnju tačku za podnošenje u nacionalni sistem e-fakturisanja (ANAF, u Rumuniji) — slanje je samo postavka naloga u korisničkom interfejsu — i nema nijednu krajnju tačku za listanje faktura.
  • Oblio može kreirati klijente i proizvode samo kao sporednu posljedicu izdavanja dokumenta, a ne kao resurse prvog reda.
  • Generisanje UBL XML-a bez podnošenja (CIUS-RO za Rumuniju): dobijate pravni dokument kao fajl, bez dodirivanja nacionalnog sistema.
  • Idempotentnost fiskalnih zapisa: ponovni pokušaj nakon isteka vremena ne proizvodi drugu fakturu niti troši drugi broj.
  • Mašinski čitljivo ulazno sanduče faktura dobavljača — osnova za automatsko usklađivanje.

Vaša prva faktura u 6 koraka

Prije prvog poziva
  1. Kreirajte kredencijal u panelu pod Fakturisanje → Postavke → API pristup i kopirajte client_secret — prikazuje se jednom.
  2. Popunite profil kompanije i aktivirajte bar jednu seriju tipa fiscale; inače izdavanje vraća issuer_profile_incomplete ili no_series_configured.
  1. Kreirajte kredencijal u panelu pod Fakturisanje → Postavke → API pristup i kopirajte client_secret — prikazuje se jednom.
  2. Popunite profil kompanije i aktivirajte bar jednu seriju tipa fiscale; inače izdavanje vraća issuer_profile_incomplete ili no_series_configured.
  3. Zamijenite kredencijal tokenom: POST /v1/auth/token sa grant_type client_credentials. Token važi jedan sat — keširajte ga, ne tražite novi za svaki zahtjev.
  4. Izdajte fakturu: POST /v1/invoices sa zaglavljem Idempotency-Key i vat_rate kao procentom (21, ne 0,21).
  5. Pokrenite prethodnu provjeru (CIUS-RO za Rumuniju) sa POST /v1/invoices/{id}/efactura/validate, zatim je pošaljite u nacionalni sistem e-fakturisanja sa POST /v1/invoices/{id}/efactura/submit.
  6. Pratite stanje sa GET /v1/efactura/submissions/{id} dok ne dostigne accepted ili rejected.
bash
# 1 — token (1 sat, keširajte ga)
ACCESS_TOKEN=$(curl -sS -X POST https://api.4invoices.net/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"grant_type":"client_credentials","client_id":"cli_XXX","client_secret":"sec_XXX"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')

# 2 — izdajte fakturu (Idempotency-Key je OBAVEZAN)
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{"buyer":{"name":"Exemplu Comert SRL","tax_id":"RO12345678","county":"Cluj","city":"Cluj-Napoca","country":"RO"},
       "lines":[{"description":"Abonament SaaS","quantity":1,"unit_price":400,"vat_rate":21}]}'

# 3 — preflight CIUS-RO, then send to ANAF
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate \
  -H "Authorization: Bearer $ACCESS_TOKEN"

curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'
javascript
const BASE = "https://api.4invoices.net/v1";

const token = await fetch(`${BASE}/auth/token`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    grant_type: "client_credentials",
    client_id: process.env.FOURINVOICES_CLIENT_ID,
    client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
  }),
}).then((r) => r.json()).then((d) => d.access_token);

const invoice = await fetch(`${BASE}/invoices`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    "Idempotency-Key": `order-${orderId}-invoice`,
  },
  body: JSON.stringify({
    buyer: { name: "Exemplu Comert SRL", tax_id: "RO12345678", county: "Cluj", city: "Cluj-Napoca", country: "RO" },
    lines: [{ description: "Abonament SaaS", quantity: 1, unit_price: 400, vat_rate: 21 }],
    currency: "RON",
    metadata: { order_id: String(orderId) },
  }),
}).then((r) => r.json());

console.log(invoice.number, invoice.totals); // "FCT-000123" { net, vat, gross }
python
import os, uuid, requests

BASE = "https://api.4invoices.net/v1"

token = requests.post(f"{BASE}/auth/token", json={
    "grant_type": "client_credentials",
    "client_id": os.environ["FOURINVOICES_CLIENT_ID"],
    "client_secret": os.environ["FOURINVOICES_CLIENT_SECRET"],
}, timeout=30).json()["access_token"]

headers = {
    "Authorization": f"Bearer {token}",
    # Jedan ključ po LOGIČKOJ operaciji — ponovo ga koristite pri ponovnom pokušaju, nikada ga ne generišite iznova.
    "Idempotency-Key": f"order-{order_id}-invoice",
}

invoice = requests.post(f"{BASE}/invoices", headers=headers, json={
    "buyer": {"name": "Exemplu Comert SRL", "tax_id": "RO12345678",
              "county": "Cluj", "city": "Cluj-Napoca", "country": "RO"},
    "lines": [{"description": "Abonament SaaS", "quantity": 1,
               "unit_price": 400, "vat_rate": 21}],   # procenat, ne 0,21
    "currency": "RON",
}, timeout=60).json()

print(invoice["number"], invoice["totals"])

Autentifikacija

  • Tok je client_credentials: zamjenjujete client_id + client_secret za Bearer access_token.
  • Token ističe za 3600 sekundi. Osvježite ga prije isteka, a ne pri svakom pozivu — POST /auth/token ima sopstveno ograničenje po IP adresi.
  • client_secret se čuva samo kao hash i ne može se povratiti. Ako ga izgubite, kreirate novi kredencijal i opozovete stari.
  • Samo server-server. CORS ne propušta nijedan izvor iz pregledača: iz JavaScript-a stranice tajna bi procurila posjetiocu.
  • Opsezi se dodjeljuju po kredencijalu. Izdajte zaseban kredencijal za svaki sistem i dajte mu minimum opsega koji mu treba.

Fakturisanje → Postavke → API pristup

bash
curl -sS -X POST https://api.4invoices.net/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "cli_XXXXXXXXXXXX",
    "client_secret": "sec_XXXXXXXXXXXXXXXXXXXX"
  }'

# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600,
#    "scope":"invoices:read invoices:write efactura:submit"}
javascript
// Keširajte token; ne pravite novi za svaki zahtjev.
let cached = { token: "", expiresAt: 0 };

async function getToken() {
  if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;

  const res = await fetch("https://api.4invoices.net/v1/auth/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "client_credentials",
      client_id: process.env.FOURINVOICES_CLIENT_ID,
      client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
    }),
  });
  if (!res.ok) throw new Error(`token ${res.status}`);

  const data = await res.json();
  cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
  return cached.token;
}

PDV stope su procenti

vat_rate: 21 ✓  ·  vat_rate: 0.19 ✗  ·  vat_rate: 1 ✗
  • vat_rate je PROCENAT: 21 znači 21%.
  • Vrijednosti u (0, 1] se odbijaju — 0,19 je razlomak, a 1 je nejasno između 1% i 100%.
  • 0 je važeće i znači oslobođeno ili reverse charge; navedite pravnu napomenu u notes.
  • unit_price je NETO jedinična cijena kada je izdavalac obveznik PDV-a.
  • Trebaju vam ukupni iznosi prije potvrde? POST /v1/invoices/preview-totals radi istu aritmetiku bez trošenja fiskalnog broja.

Idempotentnost fiskalnih zapisa

  • Zaglavlje Idempotency-Key je OBAVEZNO na POST /invoices, POST /invoices/{id}/storno i POST /invoices/{id}/efactura/submit.
  • Ključevi se pamte 24 sata. Ponavljanje istog ključa sa istim tijelom vraća originalni rezultat i postavlja Idempotency-Replayed: true.
  • Isti ključ sa drugačijim tijelom vraća 409 idempotency_key_reuse — već izdat fiskalni dokument se nikada ne prepisuje.
  • Jedan ključ po LOGIČKOJ operaciji, ne po HTTP pokušaju: order-10457-invoice, a ne novi UUID pri svakom ponovnom pokušaju.
  • Razlog je pravni, ne tehnički: utrošen fiskalni broj i faktura podnesena u nacionalni sistem e-fakturisanja ne mogu se povući.
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{
    "buyer": {
      "name": "Exemplu Comert SRL",
      "tax_id": "RO12345678",
      "address": "Str. Fabricii 12",
      "city": "Cluj-Napoca",
      "county": "Cluj",
      "country": "RO",
      "email": "[email protected]"
    },
    "lines": [
      { "description": "Abonament SaaS, iulie", "quantity": 1, "unit_price": 400, "vat_rate": 21, "unit": "buc" },
      { "description": "Implementare", "quantity": 6, "unit_price": 250, "vat_rate": 21, "unit": "ora" }
    ],
    "currency": "RON",
    "document_kind": "factura",
    "due_date": "2026-08-15",
    "metadata": { "order_id": "10457" }
  }'

Sve krajnje tačke

MetodKrajnja tačkaOpsegIdempotency-KeyZahtjeva / minut
POST/auth/token
Razmjena kredencijala za token pristupa
——120
GET/me
Klijent, opsezi i trenutna ograničenja
invoices:read—120
POST/invoices
Izdaje fakturu ili predračun
invoices:writeobavezno30
GET/invoices
Listanje faktura (paginacija po kursoru)
invoices:read—120
POST/invoices/preview-totals
Računa ukupne iznose bez kreiranja bilo čega
invoices:read—120
GET/invoices/{invoice_id}
Dohvata jednu fakturu
invoices:read—120
GET/invoices/{invoice_id}/pdf
Preuzima PDF fakture
invoices:read—120
GET/invoices/{invoice_id}/xml
Preuzima UBL XML (CIUS-RO za Rumuniju)
invoices:read—120
POST/invoices/{invoice_id}/storno
Izdaje knjižno odobrenje (storno) koje poništava ovu fakturu
invoices:writeobavezno30
POST/invoices/{invoice_id}/payments
Označava fakturu kao plaćenu
invoices:write—30
DELETE/invoices/{invoice_id}/payments
Označava fakturu kao neplaćenu
invoices:write—30
GET/clients
Listanje klijenata
clients:read—120
POST/clients
Kreira ili ažurira klijenta
clients:write—30
GET/lookup/company/{tax_id}
Provjerava poreski broj u službenom registru (u Rumuniji: ANAF)
clients:read—120
GET/series
Listanje serija numeracije
invoices:read—120
POST/invoices/{invoice_id}/efactura/validate
Prethodna provjera — provjerava bez slanja
efactura:read—120
POST/invoices/{invoice_id}/efactura/submit
Podnosi fakturu u nacionalni sistem e-fakturisanja
efactura:submitobavezno10
GET/efactura/submissions
Listanje podnesaka u nacionalni sistem
efactura:read—120
GET/efactura/submissions/{submission_id}
Dohvata jedan podnesak
efactura:read—120
POST/efactura/submissions/{submission_id}/refresh
Traži trenutno stanje od nacionalnog sistema
efactura:read—10
GET/efactura/submissions/{submission_id}/download
Preuzima arhivu potpisanog odgovora
efactura:read—120
GET/efactura/inbox
Fakture dobavljača primljene preko nacionalnog sistema
efactura:read—120
GET/efactura/inbox/{inbound_id}
Dohvata jednu dolaznu fakturu
efactura:read—120
GET/efactura/inbox/{inbound_id}/download
Preuzima arhivu dolazne fakture
efactura:read—120
POST/efactura/inbox/sync
Povlači nove poruke nacionalnog sistema u ulazno sanduče
efactura:read—10

25 endpoints · https://api.4invoices.net/v1/openapi.json

Opsezi

  • invoices:read
  • invoices:write
  • clients:read
  • clients:write
  • efactura:read
  • efactura:submit

Ciklus nacionalnog e-fakturisanja

not_sentvalidatingqueuedsentprocessingacceptedrejectederror
  • Stvarna prednost: cijeli ciklus nacionalnog e-fakturisanja radi preko API-ja, a ne samo generisanje dokumenta.
  • POST /invoices/{id}/efactura/validate vrši prethodnu provjeru (CIUS-RO za Rumuniju) i vraća tačna polja koja bi nacionalni sistem e-fakturisanja odbio, bez slanja bilo čega.
  • GET /invoices/{id}/xml generiše UBL XML (CIUS-RO za Rumuniju) bez ikakvog podnošenja — korisno za vašu arhivu ili internu reviziju.
  • POST /invoices/{id}/efactura/submit podnosi je u nacionalni sistem e-fakturisanja. Ovo je nepovratno: druga kopija iste fakture ne može se povući iz sistema.
  • Životni ciklus je normalizovan: not_sent → validating → queued → sent → processing → accepted | rejected | error. accepted znači zakonski prenesena; error je greška transporta na našoj strani i bezbjedno je ponoviti pokušaj.
  • GET /efactura/submissions/{id}/download preuzima arhivu sa potpisanim odgovorom nacionalnog sistema e-fakturisanja.
  • Postoji i prijemna strana: POST /efactura/inbox/sync povlači nove poruke iz nacionalnog sistema e-fakturisanja, a GET /efactura/inbox prikazuje fakture koje su vam poslali vaši dobavljači.
bash
# CIUS-RO preflight. Sends NOTHING to ANAF.
curl -sS -X POST "https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate?include_xml=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# → {"invoice_id":"inv_01J7ABC","valid":false,
#    "errors":["buyer.county is required for RO buyers"],
#    "xml_base64":"PD94bWwg..."}
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'

# → {"submission_id":"sub_01J7XYZ","status":"queued"}
python
import time, requests

BASE = "https://api.4invoices.net/v1"
TERMINAL = {"accepted", "rejected"}
H = {"Authorization": f"Bearer {token}"}

def wait_for_anaf(submission_id, timeout=900):
    deadline = time.time() + timeout
    delay = 15
    while time.time() < deadline:
        r = requests.get(f"{BASE}/efactura/submissions/{submission_id}", headers=H, timeout=30)
        r.raise_for_status()
        status = r.json()["status"]
        if status in TERMINAL:
            return r.json()
        # /refresh asks ANAF directly and shares the 10/min submit bucket.
        requests.post(f"{BASE}/efactura/submissions/{submission_id}/refresh", headers=H, timeout=30)
        time.sleep(delay)
        delay = min(delay * 2, 120)
    raise TimeoutError(submission_id)

Greške

  • Svaka 4xx i 5xx greška koristi isti omotač: { "error": { type, code, message, param?, doc_url, request_id, errors? } }.
  • type je opšta klasa po kojoj odlučujete („mogu li ponoviti?”), a code je stabilan razlog koji mapirate u svoj sistem.
  • param je tačkasta putanja do polja koje je problem, na primjer lines.0.vat_rate.
  • errors[] se pojavljuje kada jedan zahtjev ima probleme u više polja.
  • request_id se bilježi na našoj strani. Navedite ga u svakom zahtjevu za podršku — on određuje tačan zahtjev.
  • Za ID koji pripada drugom klijentu takođe se vraća 404: nikada ne potvrđujemo da tuđi podaci postoje.
json
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "Datele trimise nu sunt valide.",
    "param": "lines.0.vat_rate",
    "doc_url": "https://4invoices.net/ro/api-dezvoltatori#errors",
    "request_id": "req_8a893f2f33d4472da8d6e144",
    "errors": [
      { "param": "lines.0.vat_rate", "message": "VAT rate must be a percent (21), not a fraction", "type": "value_error" }
    ]
  }
}
StatusTipKod
401authentication_errormissing_bearer_token
401authentication_errorinvalid_client
401authentication_errortoken_expired
401authentication_errorinvalid_token
401authentication_errorunauthorized
403permission_errorinsufficient_scope
403permission_errorforbidden
404not_found_errornot_found
409conflict_erroridempotency_key_reuse
409conflict_erroridempotency_in_progress
409conflict_errorno_series_configured
422invalid_request_errorvalidation_failed
422invalid_request_errorissuer_profile_incomplete
422invalid_request_errorcius_ro_incomplete
422invalid_request_errorcannot_storno_draft
422invalid_request_errornothing_to_storno
422invalid_request_errorinvalid_json_number
429rate_limit_errorrate_limit_exceeded
502upstream_errorupstream_error
503service_unavailable_errorservice_unavailable

Ograničenja zahtjeva

GrupaZahtjeva / minutŠta znači
read120GET *, POST /invoices/preview-totals, POST /invoices/{id}/efactura/validate
write30POST /invoices, POST /invoices/{id}/storno, POST|DELETE /invoices/{id}/payments, POST /clients
submit10POST /invoices/{id}/efactura/submit, POST /efactura/submissions/{id}/refresh, POST /efactura/inbox/sync
  • Ograničenja važe po kredencijalu, po minuti: 120 za čitanja, 30 za pisanja, 10 za operacije e-fakturisanja (submit, refresh, inbox/sync).
  • X-RateLimit-Limit, X-RateLimit-Remaining i X-RateLimit-Reset pojavljuju se na SVAKOM odgovoru, uključujući 429.
  • 429 nosi i Retry-After u sekundama. Poštujte ga; ne ponavljajte u uskoj petlji.
  • Grupa od 10 po minuti postoji zato što u Rumuniji sam ANAF dozvoljava otprilike jedan poziv za listu poruka po minuti po CIF-u.
  • Stavite /efactura/inbox/sync na cron, a ne na agresivno ispitivanje.
  • Trebaju veće granice? Pišite preko Kontakta, sa očekivanim dnevnim obimom.

X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset · Retry-After

http
HTTP/2 429
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785312000
Retry-After: 17
Content-Type: application/json

{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded",
 "message":"...","doc_url":"https://4invoices.net/ro/api-dezvoltatori#errors",
 "request_id":"req_..."}}
javascript
// Poštujte Retry-After; nikada ne ponavljajte 4xx osim 429.
async function call(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if (res.status === 429 || res.status >= 500) {
    if (attempt >= 5) return res;
    const retryAfter = Number(res.headers.get("Retry-After")) || 2 ** attempt;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return call(url, init, attempt + 1);
  }
  return res;
}

// Ponovni pokušaj pisanja MORA koristiti ISTI Idempotency-Key, inače izdajete
// drugi fiskalni dokument.

Vodiči po temi

Svaka tema ima svoju stranicu sa relevantnim krajnjim tačkama, primjerima i greškama.

Česte greške

  • Slanje PDV-a kao razlomka (0,19) ili kao 1. Oba se odbijaju; šaljite 19 ili 21.
  • Kreiranje tokena za svaki zahtjev. Token važi sat vremena; keširajte ga, ili ćete udariti u ograničenje po IP adresi krajnje tačke za token.
  • Generisanje novog ključa idempotentnosti pri svakom ponovnom pokušaju — upravo scenario koji pravi duplikate faktura.
  • Pozivanje iz JavaScript-a u pregledaču. CORS blokira svaki izvor, a tajna bi svakako bila izložena.
  • Podnošenje u nacionalni sistem e-fakturisanja bez prethodne provjere. Pogrešno podnošenje ne može se povući.
  • Ispitivanje /efactura/submissions/{id}/refresh više od 10 puta u minuti — ta grupa se dijeli sa submit.
  • Izdavanje faktura prije nego što je profil kompanije potpun i postoji aktivna fiskalna serija.

Pitanja i odgovori za programere

Kako da dobijem API kredencijale za 4invoices?

U panelu, pod Fakturisanje → Postavke → API pristup. Dobijate client_id i client_secret; secret se prikazuje jednom i čuva se samo kao hash.

Koliko je dugo važeći access_token?

3600 sekundi, odnosno jedan sat. Keširajte ga i osvježite prije isteka — krajnja tačka za token ima sopstveno ograničenje po IP adresi.

Kako šaljem stopu PDV-a?

Kao procenat: 21 za 21%. Vrijednosti u (0, 1] se odbijaju, pa i 0,19 i 1 padaju validaciju.

Koji pozivi zahtijevaju Idempotency-Key?

POST /invoices, POST /invoices/{id}/storno i POST /invoices/{id}/efactura/submit. Ključevi se pamte 24 sata, a ponovljeni zahtjev vraća originalni dokument uz Idempotency-Replayed: true.

Mogu li fakturu poslati u nacionalni sistem e-fakturisanja direktno iz API-ja?

Da. Pokrenite prethodnu provjeru preko /efactura/validate, podnesite preko /efactura/submit, pratite normalizovani status na /efactura/submissions/{id} i preuzmite potpisani odgovor sistema (u Rumuniji: ANAF).

Koja su ograničenja zahtjeva?

Po vjerodajnici, po minuti: 120 čitanja, 30 pisanja i 10 operacija e-fakturisanja. Svaki odgovor nosi X-RateLimit-* zaglavlja, a 429 dodaje Retry-After.

Mogu li API pozivati iz svog frontenda?

Ne. Samo je server-server: nijedan izvor iz pregledača ne prolazi CORS, a client_secret ne pripada kodu koji dospijeva do posjetioca.

Zašto na prvoj fakturi dobijam issuer_profile_incomplete?

Profil kompanije je nepotpun ili nema aktivne serije tipa fiscale. Popunite oboje u panelu; API odbija da izda nevažeći fiskalni dokument.