Wymieniasz client_id i client_secret na godzinny token Bearer, wyłącznie server-to-server.
Pierwsza faktura w 6 krokach
- Wygeneruj poświadczenie w panelu w sekcji Fakturowanie → Ustawienia → Dostęp do API i od razu skopiuj client_secret — jest wyświetlany tylko raz.
- Wyślij POST /v1/auth/token z grant_type client_credentials, client_id i client_secret.
- W odpowiedzi dostajesz access_token, token_type Bearer, expires_in 3600 oraz listę przyznanych zakresów.
- Umieszczaj token w nagłówku Authorization: Bearer <access_token> w każdym kolejnym wywołaniu.
- Sprawdź, co faktycznie otrzymałeś, przez GET /v1/me: tenant, zakresy i bieżące pule limitów.
Wszystkie endpointy
| Metoda | Endpoint | Scope | Idempotency-Key | Żądania / minutę |
|---|---|---|---|---|
| POST | /auth/token Wymień dane uwierzytelniające na token dostępu | — | — | 120 |
| GET | /me Tenant, scope'y i bieżące limity | invoices:read | — | 120 |
Czym jest 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"
# → tenant, przyznane scope'y i bieżące limityjavascript
// Zapisz token w cache; nie generuj nowego przy każdym żądaniu.
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
Scope'y
- invoices:read
- invoices:write
- clients:read
- clients:write
- efactura:read
- efactura:submit
Wyłącznie server-to-server. Nigdy nie wywołuj API z JavaScriptu w przeglądarce: client_secret trafiłby do użytkownika, a CORS i tak blokuje każdy origin.
Błędy
| Status | Typ | Kod |
|---|---|---|
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 403 | permission_error | insufficient_scope |
Częste błędy
- Endpoint tokenu ma własny limit na adres IP. Jeden token na proces, odświeżany przed wygaśnięciem — nie jeden na żądanie.
- client_secret jest przechowywany wyłącznie jako skrót; jeśli go zgubisz, generujesz nowe poświadczenie i unieważniasz stare.
- Wydaj osobne poświadczenia dla ERP, sklepu i skryptów wewnętrznych, każde z minimalnymi zakresami. Unieważnienie jednego nie wpływa na pozostałe.
- CORS nie dopuszcza żadnego źródła przeglądarkowego: API z założenia działa wyłącznie serwer–serwer.
Pytania i odpowiedzi dla programistów
Co się stanie, jeśli client_secret jest błędny?
Otrzymasz 401 z kodem invalid_client. Zbyt krótki sekret zostaje odrzucony wcześniej, na etapie walidacji, z 422 validation_failed i param client_secret.
Czy istnieje osobne środowisko testowe?
Nie. Jest jeden host produkcyjny. Testuj na fakturach proforma (document_kind proforma) i przez /invoices/preview-totals, które nie zużywa numeru fiskalnego.
Czy mogę zawęzić zakresy istniejącego poświadczenia?
Tak, z panelu. Już wydane tokeny zachowują zakresy, z którymi zostały wygenerowane, do czasu wygaśnięcia, czyli najdalej w ciągu godziny.