KSeF UPO API: sprawdzanie statusu i trwały dowód odbioru

Zbuduj niezawodną obsługę UPO w API KSeF: sprawdzaj status faktury, obsługuj błędy końcowe, weryfikuj XML i archiwizuj dowód przyjęcia.

Ernest Bursa

Ernest Bursa

Founder · · 13 min czytania
Polish software engineer in a Warsaw office reviewing a green 200 invoice status and verified XML receipt beside an archive box

Żeby bezpiecznie pobrać UPO z KSeF, zachowaj numer referencyjny faktury zwrócony po wysyłce, a potem sprawdzaj jej status aż do stanu końcowego. Za przyjętą uznaj dopiero fakturę ze statusem 200 i numerem KSeF. Następnie pobierz XML UPO, sprawdź hash odpowiedzi, zgodność ze schemą i podpis XAdES, po czym zarchiwizuj dokładnie te same bajty. Poprawna odpowiedź na wysyłkę ani wygasający adres UPO nie dowodzą jeszcze, że KSeF przyjął fakturę.

Co dowodzi, że KSeF przyjął fakturę?

O przyjęciu świadczy zakończony wynik operacji asynchronicznej, a nie udane żądanie HTTP. Odpowiedź na wysyłkę zawiera referenceNumber faktury i uruchamia jej weryfikację. Nie nadaje jeszcze ostatecznego numeru KSeF ani nie potwierdza, że dokument przeszedł kontrolę w systemie.

Na tym rozróżnieniu opiera się niezawodna integracja. Endpoint wysyłkowy może odpowiedzieć poprawnie, choć faktura nadal czeka na walidację. Jeśli już wtedy oznaczysz ją jako przyjętą, lokalny stan wyprzedzi stan urzędowy. Klient zobaczy fałszywy sukces, księgowość ruszy za wcześnie, a później może zabraknąć identyfikatorów potrzebnych do znalezienia odrzucenia.

Oficjalny przewodnik po sesji interaktywnej opisuje weryfikację po wysyłce jako operację asynchroniczną. Zapisz zwrócony numer referencyjny faktury obok lokalnej faktury i numeru referencyjnego sesji od razu po wysłaniu. Każdy z tych identyfikatorów ma inne zadanie:

  • Lokalny identyfikator faktury łączy proces z rekordem biznesowym w twoim systemie.
  • Numer referencyjny sesji wskazuje sesję KSeF używaną do przesłania dokumentu.
  • Numer referencyjny faktury identyfikuje tę wysyłkę podczas przetwarzania przez KSeF.
  • Numer KSeF pojawia się dopiero po przyjęciu i staje się częścią materiału dowodowego.

Przy wyniku pozytywnym poczekaj na status faktury 200, sprawdź, czy odpowiedź zawiera numer KSeF, i pobierz UPO. Podręcznik KSeF 2.0, część II wyjaśnia, że po przyjęciu faktura dostaje numer KSeF, a UPO jest dostępne jako osobny dokument XML.

Wystarczy więc prosty model stanów: wysłana, przetwarzana, przyjęta albo odrzucona. Nie łącz wysłania z przyjęciem. Odpowiedź HTTP potwierdza transport; status 200, numer KSeF i zweryfikowane UPO potwierdzają zakończony wynik.

Który endpoint statusu KSeF odpytywać?

Gdy potrzebujesz wyniku konkretnej faktury, sprawdzaj status właśnie tej faktury. Użyj GET /sessions/{referenceNumber}/invoices/{invoiceReferenceNumber} z numerem sesji i numerem faktury zapisanymi podczas wysyłki.

Według oficjalnej specyfikacji OpenAPI odpowiedź może zawierać status faktury w KSeF, odczytany numer lokalny, numer KSeF, hash faktury, datę nadania numeru, datę przyjęcia do przetwarzania, datę trwałego zapisu, tryb fakturowania oraz wygasający adres pobrania UPO wraz z terminem ważności. Zapisuj przydatne pola, gdy tylko się pojawią, zamiast czekać na zakończenie całej sesji.

Endpoint sesji, GET /sessions/{referenceNumber}, rozwiązuje inny problem. Zwraca stan sesji oraz wartości zbiorcze, takie jak invoiceCount, successfulInvoiceCount i failedInvoiceCount. Po zamknięciu sesji może również podać referencje i adresy pobrania stron zbiorczego UPO.

Te sumy służą do uzgodnienia danych. Pokazują, czy liczba zapisanych przez ciebie wyników jednostkowych zgadza się z KSeF. Nie mówią jednak, która lokalna faktura dostała dany numer KSeF. W jednej sesji mogą też znaleźć się faktury poprawne i odrzucone, więc wynik całej sesji nie zastępuje rekordu pojedynczej faktury.

Korzystaj z obu perspektyw świadomie:

  1. Status pojedynczej faktury steruje stanem widocznym w produkcie lub systemie ERP.
  2. Status sesji służy do uzgadniania sum i, gdy ma to sens, pobierania stron zbiorczego UPO.
  3. Każdą różnicę między licznikami sesji a zapisanymi wynikami faktur trzeba wyjaśnić.

Oficjalny przewodnik po statusie sesji i UPO opisuje oba poziomy. Ich rozdzielenie chroni przed częstym błędem: traktowaniem numeru sesji, numeru referencyjnego faktury i numeru KSeF jak tej samej wartości.

Które statusy faktury są końcowe?

Mechanizm sprawdzający status musi zakończyć pracę przy każdym udokumentowanym stanie końcowym, nie tylko przy sukcesie. Kody 100 i 150 oznaczają dalsze przetwarzanie. Kod 200 oznacza powodzenie. Poniższe wyniki 4xx i 5xx wymagają obsługi błędu albo analizy.

Kod Znaczenie Klasyfikacja w produkcji
100 Faktura przyjęta do dalszego przetwarzania Stan niekońcowy
150 Trwa przetwarzanie Stan niekońcowy
200 Sukces Sukces końcowy
405 Przetwarzanie anulowane z powodu błędu sesji Błąd końcowy
410 Nieprawidłowy zakres uprawnień Błąd końcowy
415 Brak możliwości wysłania faktury z załącznikiem Błąd końcowy
430 Błąd weryfikacji pliku faktury Błąd końcowy
435 Błąd odszyfrowania pliku Błąd końcowy
440 Duplikat faktury Błąd końcowy; sprawdź ustrukturyzowane rozszerzenia
450 Błąd weryfikacji semantyki dokumentu faktury Błąd końcowy
500 Nieznany błąd Błąd końcowy; analiza ręczna
550 Operacja anulowana przez system Zbadaj przyczynę, potem ewentualnie ponów całą operację

Znaczenia pochodzą z modelu SessionInvoiceStatusResponse w aktualnym kontrakcie OpenAPI. Zapisuj otrzymany kod liczbowy, opis, szczegóły i ustrukturyzowane rozszerzenia. Tekst pomaga osobie obsługującej problem, a kod daje aplikacji stabilny punkt decyzyjny.

Szczególnej uwagi wymaga status duplikatu 440. Może zawierać uporządkowane dane o pierwotnej sesji i numerze KSeF. To materiał do analizy, nie zgoda na ciche zamienienie nowej próby w sukces. Połącz rekord z oryginałem dopiero wtedy, gdy własne reguły tożsamości faktury i porównanie hashy potwierdzą, że chodzi o ten sam dokument.

Parser powinien tolerować nieznane pola. Changelog API stwierdza, że dodatkowe właściwości mogą pojawić się bez uznania ich za zmianę łamiącą kompatybilność. Nieznany kod statusu powinien skierować fakturę do ręcznej analizy. Nigdy nie może domyślnie oznaczać przyjęcia.

Jak powinno działać sprawdzanie statusu w produkcji?

W produkcji sprawdzanie statusu musi dać się wznowić, mieć granice i przestrzegać limitów. Zapisuj każdą próbę, stosuj wykładnicze wydłużanie odstępów z losowym rozproszeniem, respektuj Retry-After, a nietypowo długo przetwarzane pozycje kieruj do kolejki uzgodnień zamiast odpytywać bez końca.

API publikuje limity, ale nie narzuca częstotliwości odpytywania. Oficjalny przewodnik po limitach podaje obecnie 30 żądań na sekundę, 120 na minutę i 1200 na godzinę dla statusu pojedynczej faktury. Pozostałe trasy /sessions/*, w tym status sesji i endpointy UPO, pozwalają na 10 żądań na sekundę, 120 na minutę i 1200 na godzinę. Odpowiedź 429 zawiera Retry-After.

To zestawienie ma znaczenie. Pętla wykonywana co sekundę może mieścić się w limicie sekundowym małego testu, a mimo to wyczerpać budżet godzinowy w produkcji. Wiele workerów może też uderzyć jednocześnie po wdrożeniu lub awarii. Losowe rozproszenie rozkłada te żądania w czasie, a zapisane next_attempt_at pozwala po restarcie wrócić do tego samego harmonogramu.

wyślij fakturę
zapisz local_id, session_reference, invoice_reference, submitted_hash

powtarzaj z ograniczonym wykładniczym wydłużaniem odstępów i losowym rozproszeniem:
  response = pobierz status faktury
  zapisz code, details, extensions, checked_at

  jeśli response to 429:
    zaplanuj następną próbę według Retry-After
  w przeciwnym razie, jeśli code to 100 lub 150:
    zaplanuj następną próbę
  w przeciwnym razie, jeśli code to 200 i istnieje ksef_number:
    zapisz ksef_number i czasy statusu
    pobierz, zweryfikuj i zarchiwizuj UPO
    zakończ jako przyjętą
  w przeciwnym razie, jeśli code to udokumentowany błąd końcowy:
    zakończ jako odrzuconą i skieruj do właściwej ścieżki naprawy
  w przeciwnym razie:
    zablokuj automatyczne przyjęcie i zażądaj ręcznej analizy

jeśli przetwarzanie przekroczy termin operacyjny:
  przenieś rekord do kolejki uzgodnień

Termin operacyjny jest zabezpieczeniem twojego systemu, a nie wymyślonym statusem KSeF. Ma przerwać nieskończoną pracę pojedynczego workera, ale zachować rekord do późniejszego sprawdzenia. Zapisuj co najmniej czas ostatniej próby, liczbę prób, szczegóły ostatniej odpowiedzi i termin następnej. To wystarczy, żeby proces przetrwał awarię.

Jak pobrać UPO faktury i sesji?

UPO faktury pobieraj po osiągnięciu przez nią statusu 200; strony zbiorczego UPO dopiero po spełnieniu warunków sesji. UPO pojedynczej faktury może być dostępne jeszcze przed zamknięciem sesji interaktywnej.

Aktualny kontrakt OpenAPI udostępnia trzy uwierzytelnione trasy:

  • GET /sessions/{sessionReferenceNumber}/invoices/{invoiceReferenceNumber}/upo
  • GET /sessions/{sessionReferenceNumber}/invoices/ksef/{ksefNumber}/upo
  • GET /sessions/{sessionReferenceNumber}/upo/{upoReferenceNumber} dla strony zbiorczego UPO

Odpowiedź statusowa może też zawierać podpisany upoDownloadUrl albo downloadUrl. Taki adres magazynu pobierz zwykłym żądaniem HTTP GET i nie dołączaj tokenu dostępowego KSeF. Opisy OpenAPI mówią, że pobrania przez podpisany adres nie podlegają limitom API i wygasają w terminie podanym w odpowiedzi.

To ważna granica bezpieczeństwa. Trasy API wymagają jednego z uprawnień wymienionych w aktualnym kontrakcie, między innymi InvoiceWrite, Introspection, PefInvoiceWrite lub EnforcementOperations. Starsze materiały opisujące pobranie z API bez uwierzytelnienia nie są dziś kontraktem wdrożeniowym. Uwierzytelnienie jest potrzebne dla trasy API KSeF; podpisany adres magazynu pobiera się bez tokenu dostępowego.

UPO faktury staje się dostępne, gdy dokument zostanie przyjęty i dostanie numer KSeF. Zbiorcze UPO pojawia się po zamknięciu sesji i przetworzeniu wszystkich dokumentów, gdy co najmniej jeden z nich ma numer KSeF i został trwale zapisany. Dlatego zamknięcie sesji interaktywnej nie powinno być warunkiem pobrania dostępnego już UPO pojedynczej faktury.

Jak zweryfikować UPO przed archiwizacją?

Archiwizuj UPO dopiero po sprawdzeniu dokładnych bajtów odpowiedzi względem hasha transportowego, schemy XML, podpisu i własnego rekordu wysyłki. Samo poprawne sparsowanie XML jest przydatne, ale nie stanowi pełnego testu integralności ani tożsamości dokumentu.

Każda poprawna odpowiedź z fakturą lub UPO udostępnia x-ms-meta-hash, czyli hash SHA-256 dokumentu zakodowany w Base64. Odczytaj body jako bajty, policz SHA-256 na niezmienionej treści, zakoduj wynik w Base64 i porównaj z nagłówkiem, zanim zaczniesz przekształcać lub normalizować XML.

Następnie sprawdź dokument względem oficjalnej schemy UPO v4-3. UPO v4-3 jest wersją domyślną od 22 grudnia 2025 roku i korzysta z jednej schemy dla UPO faktury i sesji. Zawiera między innymi TrybWysylki, który rozróżnia tryby Online i Offline. Changelog API dokumentuje zmianę wersji i zachowanie hasha.

Potem zweryfikuj podpis XAdES oraz łańcuch zaufania przy użyciu właściwych materiałów Ministerstwa Finansów. Na końcu porównaj pola biznesowe podpisanego dokumentu z rekordem wysyłki: numer sesji, hash faktury, NIP sprzedawcy, lokalny numer faktury, numer KSeF, datę wystawienia, czasy przesłania i nadania numeru oraz tryb wysyłki.

Każdy z tych testów wykonuj osobno i zachowuj jego wynik. Poprawny hash odpowiedzi dowodzi, że zapisano bajty przekazane w tej odpowiedzi. Walidacja schemy potwierdza zgodność struktury. Walidacja podpisu sprawdza autentyczność i integralność w ramach modelu zaufania. Porównanie pól biznesowych potwierdza, że dokument dotyczy faktury, którą system zamierzał obsłużyć.

Co powinien zawierać rekord dowodowy KSeF?

Trwały rekord KSeF powinien zachować identyfikatory, historię stanów, dokładne bajty UPO oraz wyniki weryfikacji. Sam numer KSeF nie pozwala odtworzyć, skąd system wziął swoją decyzję.

Pole Po co je zachować
Lokalny klucz główny i numer biznesowy faktury Łączy dowód KSeF z rekordem księgowym
Hash dokładnego XML FA(3) wysłanego do KSeF Identyfikuje dokument i pomaga sprawdzać duplikaty
Numery referencyjne sesji i faktury Pozwalają sprawdzać status, pobierać UPO i analizować zgłoszenia do wsparcia
Numer KSeF Zachowuje identyfikator nadany po przyjęciu
Ostatni kod, opis, szczegóły i rozszerzenia statusu Utrwala oficjalny wynik oraz ustrukturyzowany kontekst błędu
Daty przyjęcia do przetwarzania, nadania numeru i trwałego zapisu Nie pozwala zlać różnych zdarzeń w jeden znacznik czasu
Dokładne bajty XML UPO Uniezależnia dowód od infrastruktury dostarczającej plik
Obliczony SHA-256 i x-ms-meta-hash Dokumentuje porównanie integralności odpowiedzi
Wersja schemy UPO i wynik walidacji Pokazuje, który kontrakt strukturalny sprawdzono
Wynik weryfikacji podpisu XAdES i łańcucha zaufania Dokumentuje sprawdzenie autentyczności
Czasy pobrania i weryfikacji Pokazuje, kiedy dowód został zebrany i sprawdzony
Historia prób, napraw, identyfikatorów korelacji i śledzenia Pozwala odtworzyć awarie i je diagnozować

Trzy czasy KSeF przechowuj w oddzielnych polach. invoicingDate, acquisitionDate i permanentStorageDate opisują różne zdarzenia. Nie buduj automatu stanów na ich pozornej kolejności. Podstawą decyzji pozostają kod statusu i trwałe identyfikatory.

Zachowuj również surowe bajty, nawet jeśli wyciągasz z nich pola wygodne do wyszukiwania. Klucz w magazynie obiektowym razem z hashem treści może być dobrym rozwiązaniem, o ile retencja i kontrola dostępu odpowiadają wymaganiom wobec dowodów. Wygasający adres jest tylko jedną z dróg dostarczenia obiektu, nie zastępuje samego pliku.

Jak wrócić do pracy po błędach i wygaśnięciu linku?

Odtwarzanie procesu zaczyna się od zapisanych identyfikatorów i statusu, nie od zapamiętanego podpisanego adresu. Jeśli link wygaśnie, ponownie odpytaj KSeF w uwierzytelnionym procesie i pobierz aktualną ścieżkę do dokumentu.

Po problemie transportowym lub restarcie wznów pracę na podstawie zapisanego numeru referencyjnego faktury i ostatniego stanu odpytywania. Przy 429 dokładnie respektuj Retry-After. Dla statusów 100 i 150 kontynuuj ograniczony harmonogram. Gdy pojawi się udokumentowany błąd końcowy, przestań odpytywać i uruchom ścieżkę naprawczą dopasowaną do problemu zamiast ponawiać wysyłkę w ciemno.

Status 440 potrzebuje osobnego procesu uzgodnienia. Sprawdź ustrukturyzowane rozszerzenia, znajdź pierwotną wysyłkę i porównaj lokalną tożsamość dokumentu z niezmiennym hashem faktury. Dopiero wtedy zdecyduj, czy wcześniej przyjęty dokument jest właściwym wynikiem dla lokalnej faktury. Sam duplikat nadal oznacza błąd bieżącej próby.

Używaj sum sesji jako drugiej linii kontroli. Porównuj invoiceCount, successfulInvoiceCount i failedInvoiceCount z własnymi rekordami faktur. Różnica może ujawnić utracone zadanie, wynik niezapisany po callbacku albo fakturę skierowaną do analizy ręcznej. Przewodnik po sesji i UPO podaje oficjalne pola poziomu sesji oraz warunki dostępności dokumentów.

Na 28 sierpnia 2026 roku KSeF API 2.6.1 jest najnowszą wersją oznaczoną jako wdrożona na PRD. Wersja 2.7.1 trafiła na TEST 26 sierpnia 2026 roku; wdrożenie na DEMO zaplanowano na 15 września, a na PRD na 23 września. Opisane zmiany nie modyfikują procesu statusów i UPO, ale przed tą datą dokumentacja produkcyjna nie powinna nazywać 2.7.1 wersją produkcyjną. Przy wdrożeniu i każdej późniejszej aktualizacji sprawdź oficjalny changelog.

Jak KSeF Kit domyka obieg dowodów

KSeF Kit stosuje ten sam proces oparty na dowodach wobec faktur ze Stripe. Zamienia sfinalizowane faktury na FA(3), wysyła je, czeka na przyjęcie, zapisuje UPO i wpisuje numer KSeF z powrotem do metadanych Stripe.

To wdrożenie jasno zachowuje granicę operacji asynchronicznej. Finalizacja w Stripe nie staje się deklaracją, że KSeF przyjął fakturę. KSeF Kit czeka na urzędowy wynik i zachowuje dokument, który go potwierdza. Dokumentacja procesu wysyłki pokazuje sekwencję z perspektywy produktu, a strona KSeF Kit opisuje obsługiwaną integrację ze Stripe.

Ogólna zasada jest taka sama niezależnie od tego, czy integrację budujesz, czy kupujesz: zachowaj każdą referencję, zatrzymaj automat przy każdym stanie końcowym, zweryfikuj pobrany dokument i zarchiwizuj dowód, który przetrwa swój adres URL. Dopiero wtedy udane wywołanie API staje się wynikiem, który da się później uzgodnić i obronić.

Jeśli wystawiasz faktury przez Stripe i nie chcesz samodzielnie budować odpytywania ani archiwum dowodów, zobacz, jak KSeF Kit wysyła i zapisuje faktury.

Powiazane artykuly

Gotowy na madrzejsza rekrutacje?

Zacznij za darmo na 30 dni. Zrezygnuj przed końcem, a nie zapłacisz ani grosza. Skonfiguruj swój pierwszy pipeline rekrutacyjny w kilka minut.

Zacznij za darmo