Przejdź do treści
StayPark Suite · Pomoc
StayPark Suite · Pomoc
Zamknij menu
Szukaj w pomocy Ctrl K
Wprowadzenie Przewodniki Panel - obsługa Portal i aplikacje Wdrożenie i konfiguracja Integracje i API Nowości
API synchronizacji

API synchronizacji

Jak serwer lokalny lokalizacji wymienia dane z chmurą - pobieranie (pull) z kursorami i zakresem operatora, nagrobki usunięć, wypychanie (push) z ostrzeżeniami, pliki i operacje wyłącznie online.

Integrator
Wdrożeniowiec

Zaktualizowano 2 listopada 2026

Na tej stronie
Podświetlone słowa z wyszukiwania: ()

API synchronizacji (/api/sync/v1) łączy serwer lokalny lokalizacji, czyli kasę, z chmurą. Kasa pobiera z chmury konfigurację, wysyła do niej to, co powstało na miejscu, i wykonuje operacje, które wymagają połączenia (np. wpłata na zamówienie). Ten przewodnik opisuje model wymiany. Pola i przykłady każdej operacji są w dokumentacji referencyjnej sync.yaml (gdzie ją znaleźć).

Zanim zaczniesz

  • Token synchronizacji lokalizacji z panelu: Lokalizacje i zasoby › Lokalizacje, karta lokalizacji, zakładka Synchronizacja.
  • Trwały identyfikator instalacji serwera lokalnego do nagłówka X-Sync-Installation. Zasady obu nagłówków: Uwierzytelnianie.

Cykl synchronizacji

Wymiana danych między kasą a chmurą
Opis tekstowy diagramu
  • Kasa → Chmura: GET ping (token, instalacja
  • Kasa → Chmura: POST pull z kursorami
  • Chmura → Kasa: paczka zmian, nowe kursory, has_more
  • Kasa → Chmura: POST pull, dopóki has_more
  • Kasa → Chmura: GET files/uuid (bajty nowych plików
  • Kasa → Chmura: POST push (dane z kasy
  • Chmura → Kasa: wynik dla każdego zasobu

Kasa prowadzi oba kierunki: sama pyta o zmiany i sama wysyła swoje rekordy.

Każda strona jest źródłem prawdy dla innych danych. Chmura rządzi konfiguracją: produktami, cennikami, użytkownikami, stanowiskami, regulaminami. Kasa rządzi tym, co powstało na miejscu: rachunkami, sprzedażą, płatnościami, zmianami kasowymi, ruchami magazynowymi z kasy.

GET ping sprawdza token i powiązanie instalacji. Zwraca podstawowe dane lokalizacji i schema_version chmury.

Pobieranie (pull)

POST pull zwraca przyrostowe zmiany wszystkich zasobów - łącznie najwyżej batch_limit rekordów (1-1000, domyślnie 500). Zasoby idą w kolejności zależności: rodzic przed dzieckiem. Pełna lista i kolejność są w opisie operacji pull.

  1. Wyślij localization_uuid lokalizacji tokenu, schema_version kasy i cursors z poprzedniej odpowiedzi. Przy pierwszej synchronizacji cursors jest puste.
  2. Zapisz rekordy z data.resources - każdy zapis po uuid, tak żeby powtórzony rekord niczego nie dublował.
  3. Zapisz wszystkie kursory z data.cursors, także te dla zasobów bez zmian.
  4. Powtarzaj pull, dopóki data.has_more = true.

Kursory

Kursor to para chwila w UTC i id rekordu, zapisana jako chwila#id, np. 2026-10-25T00:50:00Z#123. Sam czas nie wystarcza: rekordy z tej samej sekundy na granicy paczki by przepadły. Starszy zapis kursora (sam czas, czas z przesunięciem +02:00) chmura przyjmuje jako (czas, 0).

Kursor nie mija rekordów młodszych niż 3 sekundy. Transakcja zatwierdzona chwilę po odczycie może nieść wcześniejszy znacznik czasu i inaczej zginęłaby na zawsze. Dlatego taki rekord przychodzi w paczce, a potem jeszcze raz. Gdy cała paczka jest świeża, has_more ma wartość false - kursor stoi i nie ma sensu pytać od razu o tę samą paczkę.

Kształt rekordów

  • Numer id jest usuwany. Klucze obce przychodzą jako uuid, np. operator_id → operator_uuid.
  • Daty są w czasie ściennym strefy aplikacji (Europe/Warsaw), bez przesunięcia: kolumna daty jako 2026-11-05, data z godziną i znaczniki created_at, updated_at, deleted_at jako 2026-11-05 10:00:00. Wyjątek to zasoby z własnym ładunkiem (marketing_pos_rules, coupon_pool_entries, marketing_contents, ticket_entitlements) - mają daty ISO 8601 z przesunięciem. Kursor zawsze bierz z cursors odpowiedzi, nigdy z dat rekordów.
  • Zasoby lokalizacji (kasetki, terminale, magazyny, cenniki) przychodzą tylko dla lokalizacji tokenu. users obejmuje tylko aktywnych pracowników lokalizacji.
  • Wybrane zasoby mają własny zakres, np. warehouse_items bez ilości (stan na kasie to suma ruchów), invoices tylko z metadanymi, regulations z gotowym HTML-em i version_hash.
  • Rekord, którego chmura nie zdoła przygotować do wysłania, jest pomijany, a reszta paczki schodzi normalnie. Wróci przy następnej zmianie tego rekordu.

Zakres operatora

Kasa dostaje dane wyłącznie operatora swojej lokalizacji:

Zasób Co przychodzi
operators tylko operator lokalizacji tokenu
localizations lokalizacje tego operatora, zawsze także własna
files pliki tego operatora i pliki wspólne (bez operatora)

Rekord innego operatora nie znika z odpowiedzi, tylko przychodzi jako { uuid, _op: delete, updated_at, deleted_at }. Dzięki temu kasa usuwa kopię pobraną przed zawężeniem zakresu. Odsyłacz do takiego rekordu w innych zasobach, np. brands.owner_operator_uuid albo logo_image_uuid, przychodzi jako null - kasa nie czeka na rodzica, którego nigdy nie dostanie. Pozostałe zasoby katalogu (marki, produkty, kategorie) nadal przychodzą wszystkie.

Po przeniesieniu lokalizacji do innego operatora rekordy nowego operatora nie zejdą same przy pobraniu przyrostowym. Kasę trzeba wtedy zsynchronizować od nowa (pełne pobranie bez kursorów).

Jednorazowe pełne pobranie po aktualizacji (listopad 2026)

Chmura w wersji z 2026-11-02 zmieniła dwie rzeczy w pobraniu: format dat (wcześniej UTC z Z, teraz czas ścienny) i zakres operatora. Rekordy pobrane wcześniej mają więc daty przesunięte o strefę, a rekordy innych operatorów usuną się dopiero przy ponownym wysłaniu. Dlatego kasa po aktualizacji raz kasuje kursory pobrania i pobiera cały zakres od nowa. Kursory wysyłki zostają - dane czekające na wysłanie nie giną.

Kolejność wdrożenia: najpierw chmura, potem kasy. Kasa zaktualizowana przed chmurą pobrałaby cały zakres jeszcze w starym formacie.

Usunięcia i nagrobki

Rekord usunięty w chmurze przychodzi jako { "uuid": "...", "_op": "delete" }. Źródła są dwa:

Źródło Kiedy Kursor
miękkie usunięcie rekordu zwykłe zasoby z deleted_at kursor zasobu
nagrobek (wpis w dzienniku usunięć) wiersze kasowane twardo, np. powiązania grup uprawnień, pozycje magazynu, produkty lokalizacji, faktury usunięte trwale, pracownik przeniesiony do innej lokalizacji osobny kursor <zasób>:deletions

Nagrobek może dotyczyć jednej lokalizacji. Gdy cennik, magazyn albo pracownik przejdzie do innej lokalizacji, stara lokalizacja dostaje delete, a nowa - zwykły rekord. Kasa przy delete cennika i magazynu nie kasuje rekordu, tylko go wyłącza, bo odwołują się do niego rachunki i ruchy. Plik z magazynu prywatnego zawsze przychodzi jako delete.

Wypychanie (push)

POST push przyjmuje rekordy zasobów, dla których źródłem prawdy jest kasa. Chmura zapisuje każdy rekord osobno, po uuid: rekord z kasy nadpisuje kopię w chmurze. Ponowne wysłanie tego samego rekordu jest bezpieczne.

  • Wysyłaj rodzica przed dzieckiem, w kolejności z opisu operacji (np. bills przed bill_items, sales przed sale_items).
  • Klucze obce wysyłaj jako *_uuid. Lokalizację ustala chmura z tokenu.
  • warehouse_movements to wyjątek: chmura przyjmuje je jako zmianę stanu, a nie nadpisanie. Powtórzony ruch o tym samym uuid nie zmienia stanu drugi raz.
  • Jedno żądanie mieści najwyżej 500 rekordów łącznie, inaczej chmura zwraca 400 BATCH_LIMIT_EXCEEDED.

Błędny rekord nie przerywa paczki. Odpowiedź ma dla każdego zasobu liczniki received, upserted, deleted, skipped, listę errors z uuid i powodem oraz - gdy jest co zgłosić - listę warnings (niżej):

Powód Zasoby Co zrobić
UNRESOLVED_RELATION wszystkie chmura nie zna jeszcze rekordu, do którego się odwołujesz - wyślij ponownie później
PENDING_DATA bill_transfers przejęcie rachunku czeka na komplet danych - wyślij ponownie
CODE_COLLISION ticket_entitlements kod biletu jest już zajęty w chmurze - nadaj nowy kod i wyślij ten sam uuid ponownie
MISSING_UUID, UNKNOWN_RESOURCE wszystkie błąd po stronie kasy - popraw rekord
UNKNOWN_TYPE warehouse_movements, sale_returns nieznany rodzaj ruchu albo rodzaj, dokument lub sposób wypłaty zwrotu - popraw rekord
MISSING_TAX_RATE sale_items pozycja bez stawki VAT - chmura nie podstawia zera, bo 0% to też stawka
INVALID_RECORD kuchnia, bramki, akceptacje regulaminów, liczniki i wydruki kampanii brak wymaganych pól albo zła data
INVALID_ACCESS_CODE, MISSING_SALE, DUPLICATE_UNIT ticket_entitlements kod spoza formatu, bilet bez sprzedaży i jej pozycji, drugi bilet dla tej samej sztuki
INVALID_STATUS ticket_entitlement_redemptions nieznany stan realizacji
DUPLICATE_SALE marketing_receipt_prints druga wydrukowana kampania do tej samej sprzedaży
UNKNOWN_REQUEST kitchen_requests odpowiedź na żądanie kuchni nieznane albo innej lokalizacji
TOTAL_MISMATCH sale_returns kwoty zwrotu nie wynikają z pozycji korekty - operator dostaje alarm
RETURN_EXCEEDS_SALE sale_returns łączne zwroty przekraczają wartość sprzedaży - operator dostaje alarm
FOREIGN_RECORD, FOREIGN_WAREHOUSE wszystkie, warehouse_movements rekord innej lokalizacji albo z panelu - chmura go pomija
SETTLED_RECEIVABLE payments należność rozliczona w chmurze - nie da się jej usunąć
SYNC_ERROR wszystkie wyjątek poza obsługą rekordu; chmura powtarza wtedy paczkę zasobu rekord po rekordzie, a ten rekord dalej zawodzi - wyślij ponownie
ERROR wszystkie inny błąd przetwarzania; w trybie debugowania chmury zamiast ERROR przychodzi treść błędu

Ponowienie bez zmian ma sens tylko przy UNRESOLVED_RELATION, PENDING_DATA i SYNC_ERROR (oraz przy CODE_COLLISION po nadaniu nowego kodu). Pełna lista powodów jest w schemacie PushResourceResult w dokumentacji referencyjnej.

Rekordy przyjęte z ostrzeżeniem

Rekord z listy warnings jest zapisany i liczy się w upserted. Kasa go nie ponawia - ostrzeżenie mówi tylko, że chmura czegoś z niego nie wzięła albo założyła alarm. Każdy wpis ma uuid i reason:

Powód Zasoby Co znaczy
CANCELLATION_MISMATCH, CANCELLATION_TOTAL_MISMATCH sale_returns wypłata zwrotu z rezygnacji nie pasuje do dokumentu rezygnacji; operator dostaje alarm
SALE_PROCESSED_LOCKED sales, sale_items, payments sprzedaż jest już przetworzona w chmurze, więc część zmian z kasy pominięto; lista pól jest w fields, opis po polsku w message

Sprzedaż przetworzona w chmurze

Gdy sprzedaż ma w chmurze stan processed albo completed, chmura wykonała już jej skutki, np. wystawiła fakturę i wykonała czynności sprzedaży. Od tej chwili o polach, od których te skutki zależą, decyduje chmura. Push pomija zmiany:

  • w sales - statusu, kwot, rodzaju dokumentu, danych stron, klienta, marki, mnożnika punktów, korekty i anulowania;
  • w sale_items - towaru, stawki, ilości i kwot;
  • w payments - formy, kierunku i kwoty płatności, także _op: delete.

Pozostałe pola rekordu się zapisują, np. numer paragonu i dane e-paragonu. Pominięte pola wracają w warnings z powodem SALE_PROCESSED_LOCKED i listą fields. Nowe pozycje i płatności przetworzonej sprzedaży chmura przyjmuje normalnie. Kasa loguje ostrzeżenia i pokazuje je w podsumowaniu paczki, ale ich nie ponawia.

Promocje i kody ponad limit

Kasa bez połączenia liczy limit użyć promocji z ostatniego pobrania, a rezerwacja kodu wygasa po 12 godzinach. Rozliczenie w coupon_redemptions może więc przekroczyć limit promocji, kodu albo limit kodu na klienta. Chmura takie rozliczenie przyjmuje - rabat już udzielono - i liczniki rosną ponad limit. Operator dostaje ostrzeżenie „Promocja lub kod użyte ponad limit” (PROMOTION_USAGE_OVER_LIMIT): najwyżej jedno otwarte na promocję, kod i parę kod-klient. Odpowiedź nie ma przy tym błędu ani ostrzeżenia, a ponowny push tego samego wpisu niczego nie dubluje.

Zwroty z kasy

Wpis sale_returns musi mieć kwoty wynikające z pozycji korekty, liczone tak samo jak przy zwrocie w panelu: total_gross_minor to wartość wszystkich pozycji korekty razem z kaucjami, total_net_minor - pozycje bez opakowań zwrotnych, a total_deposit_minor - same opakowania zwrotne, wszystko ze znakiem dodatnim. Inne kwoty dają TOTAL_MISMATCH. Gdy łączne zwroty sprzedaży (bez anulowanych korekt) przekroczyłyby jej wartość brutto z kaucją, wpis dostaje RETURN_EXCEEDS_SALE. Zwrot na kasie jest już wtedy wypłacony, więc chmura zakłada alarm dla operatora - ponowienie tego samego wpisu nic nie zmieni.

Każdy odbiór musi być idempotentny

Kursor celowo wysyła świeże rekordy dwa razy, a kasa ponawia push po przerwie w łączności. Zapis po stronie odbiorcy - w chmurze przy push i na kasie przy pull - zawsze szukaj po uuid. Zapis „dopisz nowy wiersz” da duplikaty.

Pliki

Metadane plików (obrazy produktów, filmy ekranów, wydruki) przychodzą zasobem files w zwykłym pull. Zawartość pobiera się osobno: GET files/{uuid}.

  • Kasa dostaje wyłącznie pliki z magazynu publicznego. Plik prywatny (dokumenty z danymi klientów) daje 403 FILE_FORBIDDEN, a w pull przychodzi jako delete.
  • Duży plik można pobierać z wznowieniem: nagłówek Range: bytes=OD- daje 206 z częścią pliku, zakres poza plikiem - 416. Odpowiedź zawsze ma Accept-Ranges: bytes.
  • PDF faktury do druku idzie strumieniem GET invoices/{uuid}/pdf i nie trafia do lokalnej kopii plików.

Kasa raportuje stan swojej kopii plików przez POST files/report, a rekordy, których nie udaje się wysłać ani zapisać, przez POST failures/report (co 5 minut). Oba raporty widać w panelu w zakładce Synchronizacja lokalizacji. Gdy najstarszy odrzucony rekord czeka dłużej niż 60 minut, operator dostaje alarm.

Operacje wyłącznie online

Część operacji działa tylko przy połączeniu z chmurą - kasa nie ma ich danych u siebie:

  • rozpoznanie klienta po telefonie albo kodzie QR (customers/resolve), zgoda na e-paragon;
  • kody rabatowe (coupons/check, reserve, release) - kody nie schodzą do kasy;
  • bilety do wykorzystania (ticket-entitlements/*);
  • wpłaty, realizacje i listy zamówień (orders/*), wypłaty zwrotów z rezygnacji (order-cancellations/*);
  • przejęcie rachunku z chmury (bills/*, bill-transfers/*), blokada identyfikatora;
  • stan faktur, dokumenty do druku i należności sprzedaży (sales/*), zadania wydruku paragonów (fiscalization-jobs);
  • kontrahenci i użytkownicy lokalizacji.

Większość z nich działa w imieniu pracownika i wymaga nagłówka X-User-Uuid. Wpłaty i wypłaty najpierw rezerwują kwotę (payment-claims, payouts/.../claim), żeby dwa stanowiska nie przyjęły tych samych pieniędzy.

Limity żądań

Całe API ma limit SYNC_RATE_LIMIT_PER_MINUTE (domyślnie 600 żądań na minutę z jednego adresu IP). Część operacji online ma dodatkowo limit własny:

Limit na minutę Operacje
240 sales/{uuid}/* (faktura, dokumenty, należności, bilety), fiscalization-jobs, fiscalization-jobs/{uuid}/report
120 customers/resolve, invoices/{uuid}/pdf, ticket-entitlements/redeem, ticket-entitlements/{code}, listy i szczegóły zamówień, wypłaty zwrotów (lista i podgląd)
60 customers/{uuid}/e-receipt-consent, orders/counters

Każdy limit własny ma osobny licznik na adres IP. Żądania sales/* nie zużywają więc limitu orders/counters. Przekroczenie daje 429 HTTP_ERROR z nagłówkiem Retry-After.

Częste problemy

403 INSTALLATION_MISMATCH

Token jest powiązany z innym serwerem lokalnym. Po wymianie sprzętu kliknij Zmień serwer lokalny w zakładce Synchronizacja lokalizacji albo wygeneruj nowy token.

400 SCHEMA_VERSION_MISMATCH

Kasa ma nowszą wersję schematu niż chmura. Zaktualizuj chmurę albo cofnij wersję kasy.

Rekordy wracają w skipped z UNRESOLVED_RELATION

Rekord nadrzędny nie dotarł jeszcze do chmury. Sprawdź kolejność zasobów w żądaniu i wyślij rekord ponownie. Gdy problem trwa godzinę, operator zobaczy alarm o odrzucanych rekordach.

403 LOCALIZATION_MISMATCH

localization_uuid w treści żądania nie wskazuje lokalizacji tokenu (wielkość liter nie ma znaczenia).

Powiązane

Brak wyników