Razmjenjujete client_id i client_secret za Bearer token koji važi sat vremena, isključivo server-server.
Vaša prva faktura u 6 koraka
- Kreirajte kredencijal u panelu pod Fakturisanje → Postavke → API pristup i odmah kopirajte client_secret — prikazuje se jednom.
- Pošaljite POST /v1/auth/token sa grant_type client_credentials, client_id i client_secret.
- Dobijate access_token, token_type Bearer, expires_in 3600 i listu dodijeljenih opsega.
- Stavite token u zaglavlje Authorization: Bearer <access_token> na svakom drugom pozivu.
- Provjerite šta ste zapravo dobili sa GET /v1/me: klijent, opsezi i trenutne grupe ograničenja.
Sve krajnje tačke
| Metod | Krajnja tačka | Opseg | Idempotency-Key | Zahtjeva / minut |
|---|---|---|---|---|
| POST | /auth/token Razmjena kredencijala za token pristupa | — | — | 120 |
| GET | /me Klijent, opsezi i trenutna ograničenja | invoices:read | — | 120 |
Šta je API 4invoices
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"}bash
curl -sS https://api.4invoices.net/v1/me \
-H "Authorization: Bearer $ACCESS_TOKEN"
# → klijent, dodijeljeni opsezi i trenutne grupe ograničenja zahtjevajavascript
// 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;
}https://api.4invoices.net/v1 · https://api.4invoices.net/v1/openapi.json
Opsezi
- invoices:read
- invoices:write
- clients:read
- clients:write
- efactura:read
- efactura:submit
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.
Greške
| Status | Tip | Kod |
|---|---|---|
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 403 | permission_error | insufficient_scope |
Česte greške
- Krajnja tačka za token ima sopstveno ograničenje po IP adresi. Jedan token po procesu, osvježen prije isteka — ne jedan po zahtjevu.
- client_secret se čuva samo kao hash; ako ga izgubite, kreirate novi kredencijal i opozovete stari.
- Izdajte zasebne kredencijale za ERP, prodavnicu i interne skripte, svaki sa minimalnim opsegom. Opoziv jednog ne utiče na ostale.
- CORS ne dozvoljava nijedan izvor iz pregledača: API je po dizajnu server-server.
Pitanja i odgovori za programere
Šta se dešava ako je client_secret pogrešan?
Dobijate 401 sa kodom invalid_client. Previše kratka tajna pada ranije, u validaciji, sa 422 validation_failed i param client_secret.
Postoji li poseban sandbox?
Ne. Postoji jedan produkcijski host. Testirajte sa predračunima (document_kind proforma) i sa /invoices/preview-totals, koji ne troši fiskalni broj.
Mogu li suziti opsege postojećeg kredencijala?
Da, iz panela. Tokeni koji su već izdati zadržavaju opsege sa kojima su kreirani dok ne isteknu, u roku od sat vremena.