Dane dostępowe do sieci
Zapisywanie i wymiana danych dostępowych klienta do sieci.
- W sandboxieprzechowywanie
- Jeszcze nie w sandboxiesieci
Prostymi słowami
Dane dostępowe do sieci to klucz lub autoryzacja, dzięki którym możemy działać w sieci lub w systemie administracji danego kraju w imieniu jednego klienta, na przykład token do Krajowego Systemu e-Faktur (KSeF). Klient je wydaje lub autoryzuje, a do eurinvoice trafiają przez API, nigdy e-mailem ani przez czat. Przechowujemy je w postaci zaszyfrowanej i nigdy więcej ich nie pokazujemy. Hostowany sandbox nie przechowuje jeszcze żadnych, więc kanał tam niczego nie wysyła.
Dane dostępowe do sieci pozwalają nam działać w kanale przesyłania w imieniu jednego klienta: token KSeF, autoryzacja ANAF, klucz platformy. Trafiają do usługi przez to API, nigdy e-mailem ani przez czat. API je przechowuje i nigdy ich nie zwraca. Wywołania wymagają klucza z zakresem admin, a klucz klienta zapisuje dane dostępowe tylko dla własnego klienta.
Jak przechowywana jest wartość
Wartość jest szyfrowana przed zapisaniem, przechowywana oddzielnie od klucza, który ją chroni, i nigdy więcej nie jest pokazywana. Dane dostępowe są odszyfrowywane tylko wtedy, gdy usługa miałaby wywołać kanał tego klienta. Każde użycie zapisuje wiersz audytu bez wartości.
Dla danych dostępowych, których expires_on przypada za mniej niż 30 dni, zapisywany jest alert. Unieważnienie usuwa wartość i zachowuje daty.
Pola
| Pole | Znaczenie |
|---|---|
client | Identyfikator klienta, ten sam, który zawiera faktura: od 1 do 64 liter, cyfr, kropek, podkreślników lub myślników. Klucz klienta może pominąć to pole. |
route | DE-XRECHNUNG, PEPPOL, FR-PA, PL-KSEF lub RO-EFACTURA. |
kind | Nazywa dane dostępowe i decyduje o sposobie ich odczytu, maksymalnie 80 znaków: litery, cyfry, kropki, podkreślniki i myślniki. ksef-token dla KSeF i anaf-oauth dla ANAF. Dla kanału Peppol lub francuskiego podajemy rodzaj i kształt wartości, gdy rozpoczyna się uruchomienie. |
issued_by | Kto je wydał, maksymalnie 200 znaków. |
issued_on | Data wydania, YYYY-MM-DD. |
expires_on | Opcjonalne, YYYY-MM-DD. Należy je ustawić, jeśli dane dostępowe mają termin ważności. |
environment | Opcjonalne. Musi być środowiskiem wywoływanej usługi; sandbox zapisuje tylko dane dostępowe sandboxa. |
legal_entity | Opcjonalne, maksymalnie 64 znaki. Identyfikator sprzedawcy, w imieniu którego działają dane dostępowe: numer VAT, NIP, SIREN lub identyfikator firmy. Dane dostępowe bez niego obsługują pozostałe faktury klienta. |
value | Sekret, maksymalnie 16 384 znaki. Wymagany i nigdy nie zwracany. |
Usługa sprawdza, czy wartość może działać dla swojego rodzaju, na przykład czy token KSeF to JSON z polami nip i token. Wartość anaf-oauth to tekst JSON z polem cui oraz albo z access_token, albo z refresh_token wraz z client_id i client_secret aplikacji. Wartość, która nie może, kończy się odpowiedzią 422, credential-unusable, a odpowiedź podaje, jakiego kształtu oczekuje, bez cytowania wartości.
Odpowiedź zawiera id (cred_ i 24 znaki szesnastkowe), powyższe pola poza value, stored (true, dopóki wartość jest przechowywana) i revoked_at po unieważnieniu.
Klient może mieć jedne aktywne dane dostępowe dla każdego kanału, kind, środowiska i podmiotu prawnego. Proces roboczy używa najstarszych nieunieważnionych danych dla klienta i kanału faktury.
Zapisanie danych dostępowych
POST /credentials przyjmuje powyższe pola jako JSON i zwraca 201. Żądanie znajduje się w pliku credential-create.json.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-create.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-create.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-create.json'),
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2027-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Ponowne zapisanie tych samych danych, gdy pierwsze są aktywne, kończy się odpowiedzią 409.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-create.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-create.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-create.json'),
});
console.log(response.status);
console.log(await response.text());{
"type": "https://eurinvoice.com/problems/credential-exists",
"title": "This client already has this credential",
"status": 409
}Lista i odczyt
GET /credentials zwraca wszystkie dane dostępowe, aktywne i unieważnione, jako {"data": [...]}. GET /credentials/{id} zwraca jeden zestaw, a dla nieznanego identyfikatora 404.
curl "https://api-sandbox-eu.eurinvoice.com/credentials" \
-H "Authorization: Bearer <your-api-key>"import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials"))
.header("Authorization", "Bearer <your-api-key>")
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials', {
headers: {
Authorization: 'Bearer <your-api-key>',
},
});
console.log(response.status);
console.log(await response.text());{
"data": [
{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2027-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}
]
}Wymiana
POST /credentials/{id} przyjmuje nową value i opcjonalnie nowe expires_on. Klient, kanał, rodzaj i data wydania pozostają bez zmian. Z tego wywołania należy korzystać po odświeżeniu tokenu. Żądanie znajduje się w pliku credential-replace.json.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
--data-binary @credential-replace.jsonimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("credential-replace.json")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/json',
},
body: await readFile('credential-replace.json'),
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"kind": "anaf-oauth",
"expires_on": "2028-09-01",
"stored": true,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Unieważnienie
POST /credentials/{id}/revoke usuwa wartość i ustawia revoked_at. Powtórzenie wywołania niczego nie zmienia.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke" \
-H "Authorization: Bearer <your-api-key>"import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke"))
.header("Authorization", "Bearer <your-api-key>")
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}const response = await fetch('https://api-sandbox-eu.eurinvoice.com/credentials/cred_5bebc3172daf651a788e4284/revoke', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
},
});
console.log(response.status);
console.log(await response.text());{
"issued_by": "ANAF",
"environment": "sandbox",
"route": "RO-EFACTURA",
"revoked_at": "2026-10-06T19:49:12.987Z",
"kind": "anaf-oauth",
"expires_on": "2028-09-01",
"stored": false,
"client": "acme-srl",
"issued_on": "2026-09-01",
"id": "cred_5bebc3172daf651a788e4284"
}Ostrzeżenie
Unieważniony rekord pozostaje, na potrzeby śladu audytu. Nie można go wymienić: to kończy się odpowiedzią 409, credential-revoked. Nowe dane dostępowe należy zapisać jako nowy rekord. Unieważnione dane dostępowe nie blokują już ponownego zapisania danych dla tego samego klienta, kanału i rodzaju.
Odpowiedzi
| Status | Znaczenie |
|---|---|
200 | Lista, jeden zestaw danych dostępowych, wymiana lub unieważnienie. |
201 | Zapisano. |
400 | Treść nie jest JSON, brakuje wymaganego pola lub jest ono za długie, route nie jest jednym z pięciu kanałów albo data nie ma formatu YYYY-MM-DD. |
401 | Brak klucza lub nieznany klucz. |
403 | Klucz nie ma zakresu admin albo wskazuje innego klienta. |
404 | Brak takich danych dostępowych. |
409 | Klient ma już te dane dostępowe (credential-exists) albo zostały one unieważnione (credential-revoked). |
413 | Treść przekracza 5 MB (payload-too-large). |
422 | Wartość nie może działać dla tego rodzaju (credential-unusable). |
429 | Zbyt wiele żądań dla klucza. Należy odczekać liczbę sekund podaną w Retry-After. |
503 | Usługa nie ma klucza głównego, którym mogłaby zaszyfrować wartość (unavailable). |
Czego wymaga każdy kanał
W sandboxiez oficjalnych źródeł, sprawdzono 2 paź 2026| Kanał | Klient przekazuje | Kto je wydaje | Okres ważności |
|---|---|---|---|
PL-KSEF | Token KSeF, który pozwala wysyłać faktury (InvoiceWrite), a na potrzeby statusów także je przeglądać (InvoiceRead). Jego uprawnienia są ustalane przy generowaniu, więc zmiana wymaga nowego tokenu. Logowanie certyfikatem KSeF nie jest jeszcze obsługiwane. Zapisanie danych ksef, które nie są ksef-token, kończy się odpowiedzią 422, credential-unusable. | Administrator KSeF klienta. | Zob. uwagę poniżej. |
RO-EFACTURA | Autoryzację OAuth dla naszej zarejestrowanej aplikacji ANAF. | Osoba z uprawnieniami SPV dla numeru CUI klienta: przedstawiciel prawny lub księgowy. Loguje się do portalu ANAF kwalifikowanym certyfikatem. | Token dostępu jest ważny 90 dni, a token odświeżania 365 dni. |
PEPPOL | Zgodę na zarejestrowanie klienta jako uczestnika Peppol i weryfikację tożsamości, której wymaga punkt dostępowy. | Klient podpisuje zgodę i przechodzi weryfikację. Klucz do punktu dostępowego mamy my, więc nie ma danych dostępowych klienta do zapisania. | Klucz do punktu dostępowego należy do nas i to my go rotujemy. |
FR-PA | Własne konto klienta na jego Plateforme Agréée i dane dostępowe do API dla firmy. | Klient tworzy je na swojej platformie albo przyznaje nam dostęp. | Określa go platforma. Należy zapytać, jaki rodzaj danych wydaje. |
DE-XRECHNUNG | Nic specyficznego dla samych Niemiec. Przy wysyłce e-mailem: skrzynkę nadawczą. Przez Peppol: rejestrację w punkcie dostępowym. | Klient. | Nie dotyczy. |
Wskazówka
expires_on należy ustawić według powyższych dat, aby alert na 30 dni zadziałał, zanim wygaśnie token odświeżania.
Źródła: procedura OAuth ANAF (okresy ważności tokenów), dokumentacja API KSeF: tokeny (uprawnienia ustalane przy generowaniu tokenu).
Ostrzeżenie
Polska: źródła różnią się co do tego, jak długo ważny jest token KSeF. Podręcznik Ministerstwa Finansów (wydanie z 9 lut 2026) i strona Aplikacji Podatnika (zmieniona 31 mar 2026) podają, że tokeny służą do uwierzytelniania do 31 gru 2026. Strona pytań i odpowiedzi Ministerstwa podaje, że postanowiło ono zachować tokeny w KSeF 2.0 bez daty końcowej (odpowiedź 39). Publiczne zgłoszenie w repozytorium API KSeF Ministerstwa prosi o rozstrzygnięcie tej rozbieżności. Sprawdzono 2 paź 2026. Do czasu rozstrzygnięcia należy liczyć się z koniecznością odnowienia tokenu, jeśli Ministerstwo ustali datę końcową. Logowanie certyfikatem nie jest jeszcze obsługiwane, więc token jest jedyną drogą dostępu. Zob. podręcznik, pytania i odpowiedzi i zgłoszenie.