{"openapi":"3.0.3","info":{"title":"Akces Partner API","version":"1.5.1","description":"API dla partnerów handlowych Akces: katalog produktów z cenami netto i dostępnymi stanami oraz — dla kont z włączonymi zamówieniami — składanie i śledzenie zamówień. Format JSON, kodowanie UTF-8.\n\n**Uwierzytelnianie**: każde żądanie do `/v1/*` wymaga nagłówka `X-Partner-Key: pk_<32 znaki hex>`. Aby otrzymać klucz, napisz na [kontakt@akces.pl](mailto:kontakt@akces.pl). Dostęp może być ograniczony do uzgodnionych adresów IP. Dla kont z zamówieniami lista dozwolonych adresów IP jest obowiązkowa i obejmuje wszystkie endpointy, także katalog. API działa wyłącznie przez HTTPS (żądanie `http://` do `/v1` dostaje 403 `HTTPS_REQUIRED`).\n\n**Swagger UI**: „Try it out” działa tylko dla żądań GET (katalog, statusy zamówień) i — jeśli konto ma listę dozwolonych adresów IP — tylko z adresu z tej listy (inaczej 403).\n\n**Zamówienia** (`POST /v1/orders`, `POST /v1/orders/validate`) wymagają dodatkowo podpisu HMAC: `X-Timestamp: <czas Unix w sekundach>` oraz `X-Signature: sha256=<hex HMAC-SHA256(sekret, X-Timestamp + \".\" + ścieżka + \".\" + surowe body)>`, gdzie ścieżka to `/v1/orders` lub `/v1/orders/validate`. Okno czasowe ±300 s. Tylko wywołania serwer–serwer: żądania z nagłówkiem `Origin` (przeglądarka, Swagger UI) dostają 403 `BROWSER_NOT_ALLOWED`. Pełna instrukcja (zasady, podpis z wektorem testowym, walidacja, statusy, ponawianie): [api.akces.pl/docs/zamowienia](https://api.akces.pl/docs/zamowienia).\n\n**Limity** (na konto, osobno dla każdej grupy): katalog `GET /v1/products` i `GET /v1/products/{sku}` — wspólny limit konta, domyślnie 60 żądań/min (ustalany indywidualnie); `POST /v1/orders` — 10/min; `POST /v1/orders/validate` — 30/min; `GET /v1/orders` i `GET /v1/orders/{id}` — 60/min. Zamówienia: maks. 500 prób w oknie kroczącym 24 h (złożone i odrzucone kodem 422 łącznie), maks. 500 pozycji i 256 KB body. Po 30 nieudanych uwierzytelnieniach w ciągu minuty z jednego adresu IP wszystkie żądania z tego adresu dostają 429 do końca tej minuty. Po przekroczeniu limitu HTTP 429 (`RATE_LIMITED` albo `DAILY_LIMIT_EXCEEDED`) z nagłówkiem `Retry-After` i polem `retryAfterSeconds` (liczba sekund do ponowienia). Do masowego pobierania katalogu używaj listy z `limit=200`.\n\n**Synchronizacja katalogu**: co 15–30 min pobierz pełną listę (`limit=200`, kolejne strony). Pomiędzy nimi zmiany dociągaj przez `updatedSince` = pole `timestamp` z poprzedniej odpowiedzi minus 5 min (odpowiedź przetwarzaj idempotentnie). `updatedSince` obejmuje zmiany danych kartoteki, stanów, cen katalogowych i zdjęć, ale nie `yourPriceNetto` (promocje, oferty, rabaty konta) ani rezerwacji. Przed wysłaniem zamówienia sprawdź koszyk przez `POST /v1/orders/validate`.\n\n**Stany magazynowe** są maskowane: stany **poniżej 5** zwracamy jako 0 (ukrywamy końcówki), a stany **powyżej 2× kartonu zbiorczego (`outerCarton`)** ograniczamy do tej wartości (ukrywamy skalę zapasów). Dla produktów bez zdefiniowanego kartonu zbiorczego stan nie jest ograniczany do 2× karton. Dla kont z zamówieniami `availableStock` jest dodatkowo zaokrąglany w dół do wielokrotności `orderStep` i oznacza maksymalną ilość do zamówienia; produkty niedostępne w sprzedaży przez API mają wtedy `availableStock` 0 i `yourPriceNetto` null.\n\n**Błędy**: każda odpowiedź błędu to JSON z polami `error` (opis) i `code` (kod do obsługi w programie). Nieznana ścieżka pod `/v1` → 404 `NOT_FOUND`; nieobsługiwana metoda HTTP → 405 `METHOD_NOT_ALLOWED` z nagłówkiem `Allow`; chwilowa niedostępność → 503 `SERVICE_UNAVAILABLE` z nagłówkiem `Retry-After`. Ponawiaj po przekroczeniu czasu, błędzie sieci, 429 i 5xx; pozostałe błędy 4xx popraw przed ponowieniem.\n\n**Zgodność w ramach v1**: możemy dodawać pola w odpowiedziach, kody błędów i wartości statusów — ignoruj nieznane pola, a nieznaną wartość obsłuż jako przypadek ogólny. Zmiany niezgodne wstecz ogłaszamy mailowo z wyprzedzeniem. Historia zmian: [api.akces.pl/docs#historia](https://api.akces.pl/docs#historia).\n\n**Kontakt**: zamówienia, zmiany, anulowanie, odbiór i większe ilości — dział obsługi klienta: [hurtownia@akces.pl](mailto:hurtownia@akces.pl), tel. [22 721 18 30](tel:+48227211830). Wsparcie techniczne API: [kontakt@akces.pl](mailto:kontakt@akces.pl). Dokumentacja: [api.akces.pl/docs](https://api.akces.pl/docs).","contact":{"name":"Akces — wsparcie techniczne API","email":"kontakt@akces.pl"}},"servers":[{"url":"https://api.akces.pl","description":"Produkcja"}],"security":[{"partnerKey":[]}],"tags":[{"name":"Produkty","description":"Katalog: produkty, ceny i dostępne stany."},{"name":"Zamówienia","description":"Składanie zamówień i śledzenie statusów. POST wymaga podpisu HMAC — zob. [api.akces.pl/docs/zamowienia](https://api.akces.pl/docs/zamowienia)."},{"name":"System","description":"Stan usługi i informacje o API."}],"paths":{"/v1/products":{"get":{"tags":["Produkty"],"operationId":"listProducts","summary":"Lista produktów","description":"Zwraca stronicowaną listę aktywnych wariantów produktów, posortowaną po `sku` rosnąco. Każdy element zawiera: SKU wariantu i produktu nadrzędnego, nazwę, EAN, kategorię, kolor, pakowanie (`packaging`), jednostkę, cenę katalogową netto (`priceNetto`), Twoją cenę (`yourPriceNetto`, konta z zamówieniami), krok zamawiania (`orderStep`), dostępny stan (`availableStock`, zamaskowany), objętość kartonu (`cartonVolume`), zdjęcia (`imageUrl`, `images`) i znacznik ostatniej zmiany (`updatedAt`). Filtry można łączyć.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"Numer strony (od 1). Wartość mniejsza niż 1 albo niepoprawna jest zamieniana na 1."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100},"description":"Liczba elementów na stronie (maks. 200). Wartości spoza zakresu są przycinane."},{"name":"sku","in":"query","schema":{"type":"string"},"example":"A02-003","description":"Filtruj po dokładnym SKU wariantu."},{"name":"parentSku","in":"query","schema":{"type":"string"},"example":"A02","description":"Filtruj po dokładnym SKU produktu nadrzędnego (zwraca wszystkie jego warianty)."},{"name":"search","in":"query","schema":{"type":"string"},"description":"Szuka fragmentu tekstu w SKU wariantu lub w nazwie produktu (bez rozróżniania wielkości liter)."},{"name":"updatedSince","in":"query","schema":{"type":"string","format":"date-time"},"example":"2026-09-26T08:00:00Z","description":"Data i czas w formacie ISO 8601. Zwraca warianty, w których od podanej chwili (włącznie) zmieniły się dane kartoteki (nazwa, EAN, kategoria, kolor, pakowanie, objętość kartonu), stan magazynowy, cena katalogowa lub zdjęcia, albo które pojawiły się w katalogu. Nie obejmuje zmian `yourPriceNetto` (promocje, oferty, rabaty konta) ani rezerwacji i nie pokazuje produktów wycofanych — dlatego co 15–30 min pobieraj pełną listę. Podawaj pole `timestamp` z poprzedniej odpowiedzi minus 5 min. Niepoprawna data → 400 `VALIDATION_ERROR`."}],"responses":{"200":{"description":"Lista produktów","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductsListResponse"}}}},"400":{"description":"Niepoprawny parametr zapytania, np. `updatedSince` niebędący datą ISO 8601 (VALIDATION_ERROR).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/products/{sku}":{"get":{"tags":["Produkty"],"operationId":"getProduct","summary":"Szczegóły produktu","description":"Zwraca jeden wariant produktu po SKU — w tym samym kształcie co element listy `GET /v1/products`.","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string"},"example":"A02-003","description":"SKU wariantu (np. „A02-003”)."}],"responses":{"200":{"description":"Produkt znaleziony","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductSingleResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/orders":{"post":{"tags":["Zamówienia"],"operationId":"createOrder","summary":"Złożenie zamówienia","description":"Składa zamówienie na firmę przypisaną do klucza (firmy nie podaje się w treści). Zamówienie z API jest zawsze zakładane jako odbiór osobisty w magazynie Akces (Magazyn Akces, ul. Sławęcińska 3, 05-850 Macierzysz), płatne przelewem (termin wg warunków Twojego konta), dokument: faktura, ceny netto. Adresu dostawy nie podaje się.\n\n**Nie wywołasz go z przeglądarki ani ze Swagger UI**: wymaga podpisu HMAC liczonego z body, a żądania z nagłówkiem `Origin` dostają 403 `BROWSER_NOT_ALLOWED`. Przykłady podpisu: [api.akces.pl/docs/zamowienia#podpis](https://api.akces.pl/docs/zamowienia#podpis).\n\nReguły walidacji (sprawdzane wszystkie naraz; jeden błąd odrzuca całe zamówienie — poprawne pozycje nie są zapisywane osobno):\n\n- SKU musi istnieć i być dostępny w sprzedaży przez API; opcjonalny `ean` jest sprawdzany krzyżowo;\n- każdy SKU najwyżej raz w zamówieniu;\n- `quantity` musi być wielokrotnością `orderStep` produktu (liczba jednostek w najmniejszym zdefiniowanym opakowaniu);\n- `quantity` nie może być mniejsza niż `minOrderQuantity` produktu (minimalne ilości w kolorze — Kwiaty sztuczne / Główki kwiatowe);\n- `quantity` nie może przekraczać `availableStock` w chwili złożenia zamówienia — wartość z wcześniej pobranego katalogu mogła się zmienić.\n\n**Ceny**: zawsze obowiązuje cena Akces z chwili złożenia zamówienia (odpowiedź 201/202) — cena katalogowa z najkorzystniejszym rabatem (stały rabat konta, aktywna promocja lub wyprzedaż albo oferta), naliczana automatycznie. `unitPriceNetto` i `expectedTotalNetto` w treści są opcjonalne, tylko informacyjne i nie są porównywane z ceną Akces. Zastosowane ceny wracają w odpowiedzi (`lines[].unitPriceNetto`, `totalNetto`) — zapisz je w swoim systemie. Zatwierdzenie przez Akces (PENDING_REVIEW → ACCEPTED) nie zmienia cen ani rezerwacji.\n\n**Idempotencja**: kluczem jest `externalOrderNumber`. Ponowienie z tym samym numerem i tą samą treścią zwraca HTTP 200 z aktualnym stanem pierwotnego zamówienia (`idempotentReplay: true`) — nowe zamówienie nie powstaje. „Ta sama treść” to te same pozycje w tej samej kolejności (`sku`, `quantity`, `lineNo`, `ean`), `notes`, `contact` i `currency`; ceny informacyjne nie są porównywane. Numer użyty już w zamówieniu ACCEPTED lub PENDING_REVIEW, z inną treścią → 409. Zamówienie odrzucone przez walidację (422, REJECTED) lub przez Akces (DECLINED) nie blokuje numeru.\n\n**Ponawianie**: po przekroczeniu czasu, błędzie sieci albo odpowiedzi 5xx (500, 502, 503, 504) ponów z tym samym `externalOrderNumber` i tą samą treścią (nowy `X-Timestamp` i podpis). Zamówienie mogło już zostać złożone — ponowienie zwróci wtedy 200 z `idempotentReplay: true`. Przy 429 i 503 odczekaj co najmniej czas z nagłówka `Retry-After`, potem stosuj rosnące odstępy.","security":[{"partnerKey":[],"requestSignature":[],"requestTimestamp":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"},"example":{"externalOrderNumber":"FK/2026/000123","currency":"PLN","notes":"Odbiór we wtorek rano","lines":[{"lineNo":1,"sku":"A02-003","ean":"5907489847698","quantity":12},{"lineNo":2,"sku":"B14-01","quantity":96}]}}}},"responses":{"200":{"description":"Powtórzone żądanie (ten sam numer i ta sama treść) — aktualny stan pierwotnego zamówienia (`idempotentReplay: true`); nowe zamówienie nie powstaje.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"201":{"description":"Zamówienie przyjęte do realizacji (status ACCEPTED), towar zarezerwowany.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"202":{"description":"Zamówienie złożone i poprawne, towar zarezerwowany — czeka na zatwierdzenie przez Akces (status PENDING_REVIEW). Zatwierdzenie nie zmienia cen ani rezerwacji.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"example":{"data":{"id":"cmf2x9k0a0001abcd1234efgh","externalOrderNumber":"FK/2026/000123","status":"PENDING_REVIEW","erpOrderNumber":null,"akcesOrderNumber":"CX/0412/09/26","fulfillmentStatus":null,"currency":"PLN","totalNetto":164.04,"lines":[{"lineNo":1,"sku":"A02-003","quantity":12,"unitPriceNetto":7.67,"basePriceNetto":10.95,"discountPercent":30,"discountSource":"company","lineTotalNetto":92.04},{"lineNo":2,"sku":"B14-01","quantity":96,"unitPriceNetto":0.75,"basePriceNetto":1.5,"discountPercent":50,"discountSource":"promotion","lineTotalNetto":72}],"errors":[],"declineReason":null,"createdAt":"2026-09-24T08:15:00.000Z","updatedAt":"2026-09-24T08:15:00.000Z"},"timestamp":"2026-09-24T08:15:00.120Z"}}}},"400":{"description":"Niepoprawny JSON (INVALID_JSON) albo treść niezgodna ze schematem (VALIDATION_ERROR z listą `details`). Zamówienie nie jest zapisywane.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Brak lub nieprawidłowy klucz API (UNAUTHORIZED) albo błąd podpisu: SIGNATURE_MISSING, SIGNATURE_INVALID, TIMESTAMP_INVALID (`X-Timestamp` nie jest liczbą sekund Unix, np. podano milisekundy), TIMESTAMP_EXPIRED (zegar różni się od naszego o ponad 300 s — włącz synchronizację NTP).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Adres IP spoza listy dozwolonych (FORBIDDEN), zamówienia przez API niewłączone dla konta (ORDERS_DISABLED), żądanie z przeglądarki (BROWSER_NOT_ALLOWED) albo żądanie przez http:// zamiast https:// (HTTPS_REQUIRED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Numer użyty już w zamówieniu ACCEPTED lub PENDING_REVIEW, z inną treścią (DUPLICATE_ORDER_NUMBER). W `data` — istniejące zamówienie. Zmiany złożonego zamówienia — przez dział obsługi klienta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"413":{"description":"Body większe niż 256 KB (PAYLOAD_TOO_LARGE) — podziel zamówienie.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Brak nagłówka Content-Type albo wartość inna niż `application/json` (UNSUPPORTED_MEDIA_TYPE).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Zamówienie odrzucone przez reguły walidacji (ORDER_REJECTED, status REJECTED) — `data.errors` zawiera wszystkie błędy. Towar nie jest rezerwowany. Popraw błędy i wyślij zamówienie ponownie — możesz użyć tego samego numeru.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"},"example":{"error":"Order rejected. See errors for details.","code":"ORDER_REJECTED","data":{"id":"cmf2xa1b20002abcd1234efgh","externalOrderNumber":"FK/2026/000124","status":"REJECTED","erpOrderNumber":null,"akcesOrderNumber":null,"fulfillmentStatus":null,"currency":"PLN","totalNetto":2376,"lines":[{"lineNo":1,"sku":"A02-003","quantity":300,"unitPriceNetto":7.67,"basePriceNetto":10.95,"discountPercent":30,"discountSource":"company","lineTotalNetto":2301},{"lineNo":2,"sku":"B14-01","quantity":100,"unitPriceNetto":0.75,"basePriceNetto":1.5,"discountPercent":50,"discountSource":"promotion","lineTotalNetto":75}],"errors":[{"code":"INSUFFICIENT_STOCK","message":"Quantity of A02-003 exceeds the available quantity (availableStock).","lineNo":1,"sku":"A02-003","details":{"maxOrderable":240}},{"code":"PACKAGING_MULTIPLE","message":"Quantity 100 of B14-01 is not a multiple of the packaging unit (96).","lineNo":2,"sku":"B14-01","details":{"orderStep":96,"packaging":{"package":null,"innerCarton":96,"outerCarton":1152},"suggestedQuantities":[96,192]}}],"declineReason":null,"createdAt":"2026-09-24T08:20:00.000Z","updatedAt":"2026-09-24T08:20:00.000Z"},"timestamp":"2026-09-24T08:20:00.095Z"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"description":"Błąd podczas przetwarzania zamówienia (INTERNAL_ERROR) — ponów z tym samym `externalOrderNumber` i tą samą treścią; duplikat nie powstanie.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Usługa zamówień chwilowo niedostępna (SERVICE_UNAVAILABLE). Zamówienie mogło już zostać złożone — odczekaj czas z nagłówka `Retry-After` i ponów z tym samym `externalOrderNumber` i tą samą treścią (jeśli zamówienie dotarło, dostaniesz 200 z `idempotentReplay: true`).","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Liczba sekund do ponowienia."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Zamówienia"],"operationId":"listOrders","summary":"Lista zamówień (statusy)","description":"Zamówienia złożone przez Twoją firmę przez API, od najnowszych (wg daty utworzenia), z sekcją `pagination`. Lista obejmuje wszystkie próby, także odrzucone (REJECTED). Do śledzenia statusów użyj `updatedSince` — zwraca zamówienia zmienione od podanej chwili (także zmiany statusu realizacji); może zwrócić też zamówienia bez zmian, więc przetwarzaj odpowiedź idempotentnie. Zalecamy odpytywanie co 5–15 min z `updatedSince` równym polu `timestamp` z poprzedniej odpowiedzi minus 5 min.","parameters":[{"name":"updatedSince","in":"query","schema":{"type":"string","format":"date-time"},"example":"2026-09-24T08:00:00Z","description":"Data i czas w formacie ISO 8601. Zwraca zamówienia zmienione od tej chwili (włącznie). Niepoprawna data → 400 `VALIDATION_ERROR`."},{"name":"externalOrderNumber","in":"query","schema":{"type":"string","maxLength":50},"example":"FK/2026/000123","description":"Filtruj po numerze zamówienia w Twoim systemie. Możesz dostać kilka prób z tym numerem (np. REJECTED i ACCEPTED) — obowiązuje ta o statusie ACCEPTED lub PENDING_REVIEW."},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"Numer strony (od 1). Wartość mniejsza niż 1 albo niepoprawna jest zamieniana na 1."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Liczba zamówień na stronie (maks. 100). Wartości spoza zakresu są przycinane."}],"responses":{"200":{"description":"Lista zamówień","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersListResponse"}}}},"400":{"description":"`updatedSince` nie jest datą ISO 8601 (VALIDATION_ERROR).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/orders/validate":{"post":{"tags":["Zamówienia"],"operationId":"validateOrder","summary":"Walidacja próbna zamówienia","description":"Te same reguły co `POST /v1/orders` (produkty, pakowanie, dostępność, ceny), ale bez rezerwacji towaru i bez zapisu zamówienia. Podpis liczony tym samym algorytmem, ze ścieżką `/v1/orders/validate`. Zwraca `valid`, ceny, które zostałyby zastosowane, gdybyś złożył zamówienie w tej chwili, `orderStep` na pozycję oraz listę błędów. Służy do testów integracji i sprawdzenia koszyka przed wysłaniem.\n\nWalidacja próbna nie sprawdza, czy `externalOrderNumber` był już użyty (409 lub powtórka 200), ani limitu prób zamówień w oknie 24 h.\n\n**Nie wywołasz jej z przeglądarki ani ze Swagger UI**: wymaga podpisu HMAC liczonego z body, a żądania z nagłówkiem `Origin` dostają 403 `BROWSER_NOT_ALLOWED`. Przykłady podpisu: [api.akces.pl/docs/zamowienia#podpis](https://api.akces.pl/docs/zamowienia#podpis).","security":[{"partnerKey":[],"requestSignature":[],"requestTimestamp":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"},"example":{"externalOrderNumber":"TEST-0001","currency":"PLN","lines":[{"lineNo":1,"sku":"A02-003","ean":"5907489847698","quantity":12},{"lineNo":2,"sku":"B14-01","quantity":96}]}}}},"responses":{"200":{"description":"Wynik walidacji — także wtedy, gdy zamówienie ma błędy (`data.valid: false`, lista w `data.errors`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderValidationResponse"},"example":{"data":{"valid":true,"currency":"PLN","totalNetto":164.04,"lines":[{"lineNo":1,"sku":"A02-003","quantity":12,"unitPriceNetto":7.67,"basePriceNetto":10.95,"discountPercent":30,"discountSource":"company","lineTotalNetto":92.04,"orderStep":1,"valid":true},{"lineNo":2,"sku":"B14-01","quantity":96,"unitPriceNetto":0.75,"basePriceNetto":1.5,"discountPercent":50,"discountSource":"promotion","lineTotalNetto":72,"orderStep":96,"valid":true}],"errors":[]},"timestamp":"2026-09-24T08:10:00.080Z"}}}},"400":{"description":"Niepoprawny JSON (INVALID_JSON) albo treść niezgodna ze schematem (VALIDATION_ERROR z listą `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Brak lub nieprawidłowy klucz API (UNAUTHORIZED) albo błąd podpisu: SIGNATURE_MISSING, SIGNATURE_INVALID, TIMESTAMP_INVALID, TIMESTAMP_EXPIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Adres IP spoza listy dozwolonych (FORBIDDEN), zamówienia przez API niewłączone dla konta (ORDERS_DISABLED), żądanie z przeglądarki (BROWSER_NOT_ALLOWED) albo żądanie przez http:// zamiast https:// (HTTPS_REQUIRED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body większe niż 256 KB (PAYLOAD_TOO_LARGE).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Brak nagłówka Content-Type albo wartość inna niż `application/json` (UNSUPPORTED_MEDIA_TYPE).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/orders/{id}":{"get":{"tags":["Zamówienia"],"operationId":"getOrder","summary":"Status zamówienia","description":"Zwraca jedno zamówienie Twojej firmy po `id` z odpowiedzi `POST /v1/orders`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"cmf2x9k0a0001abcd1234efgh","description":"Pole `data.id` z odpowiedzi `POST /v1/orders`."}],"responses":{"200":{"description":"Zamówienie","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderSingleResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Zamówienie nie istnieje albo nie należy do Twojej firmy (NOT_FOUND).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/health":{"get":{"tags":["System"],"operationId":"getHealth","summary":"Stan usługi","description":"Stan usługi — bez uwierzytelniania, do monitoringu. `checks.catalog` — katalog produktów (baza danych), `checks.orders` — obsługa zamówień. `status`: `ok` — wszystko działa; `degraded` — katalog działa, zamówienia chwilowo niedostępne (HTTP 200); `error` — katalog nie działa (HTTP 503). Wynik jest odświeżany co 30 s.","security":[],"responses":{"200":{"description":"Usługa działa (`status`: `ok` albo `degraded`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Katalog nie działa (`status`: `error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"},"example":{"status":"error","service":"akces-partner-api","version":"v1","checks":{"catalog":"error","orders":"ok"},"timestamp":"2026-09-26T08:00:00.000Z"}}}}}}},"/v1":{"get":{"tags":["System"],"operationId":"getApiInfo","summary":"Informacje o API","description":"Nazwa i wersja API oraz adresy dokumentacji i specyfikacji OpenAPI. Bez uwierzytelniania.","security":[],"responses":{"200":{"description":"Informacje o API","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiInfo"}}}}}}}},"components":{"securitySchemes":{"requestTimestamp":{"type":"apiKey","in":"header","name":"X-Timestamp","description":"Czas wysłania żądania jako liczba sekund Unix (nie milisekundy). Dopuszczalne odchylenie ±300 s."},"requestSignature":{"type":"apiKey","in":"header","name":"X-Signature","description":"`sha256=` + hex(HMAC-SHA256(sekret_podpisu, X-Timestamp + \".\" + ścieżka + \".\" + surowe_body)); ścieżka = `/v1/orders` lub `/v1/orders/validate`. Sekret podpisu (`whsec_…`) przekazuje Akces osobnym kanałem. Podpisuj dokładnie te bajty, które wysyłasz. Przykłady i wektor testowy: [api.akces.pl/docs/zamowienia#podpis](https://api.akces.pl/docs/zamowienia#podpis)."},"partnerKey":{"type":"apiKey","in":"header","name":"X-Partner-Key","description":"Klucz API w formacie `pk_` + 32 znaki hex (np. `pk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4`). Aby otrzymać klucz, napisz na [kontakt@akces.pl](mailto:kontakt@akces.pl)."}},"schemas":{"Product":{"type":"object","description":"Wariant produktu. W przykładzie dane stałe prawdziwego produktu A02-003; ceny i stan są przykładowe.","required":["sku","parentSku","name","ean","category","colorName","packaging","unit","priceNetto","yourPriceNetto","orderStep","minOrderQuantity","availableStock","cartonVolume","imageUrl","images","updatedAt"],"properties":{"sku":{"type":"string","example":"A02-003","description":"SKU wariantu — kod, którym zamawiasz produkt."},"parentSku":{"type":"string","example":"A02","description":"SKU produktu nadrzędnego (wspólne dla wszystkich jego wariantów)."},"name":{"type":"string","example":"Róża pianka główka 2 cm (144 szt.)","description":"Nazwa produktu."},"ean":{"type":"string","nullable":true,"example":"5907489847698","description":"Kod kreskowy (EAN) z karty towaru. Null, jeśli towar nie ma przypisanego EAN."},"category":{"type":"string","nullable":true,"example":"Kwiaty sztuczne/Główki kwiatowe","description":"Pełna ścieżka kategorii produktu (od korzenia do liścia, poziomy rozdzielone „/”). Null, jeśli produkt nie ma kategorii."},"colorName":{"type":"string","nullable":true,"example":"#003 red","description":"Kolor wariantu. Null, jeśli nie określono."},"packaging":{"type":"object","required":["outerCarton","innerCarton","package"],"description":"Pakowanie — liczba jednostek (`unit`) na każdym poziomie opakowania. Null — poziom niezdefiniowany.","properties":{"outerCarton":{"type":"integer","nullable":true,"example":120,"description":"Karton zbiorczy."},"innerCarton":{"type":"integer","nullable":true,"example":null,"description":"Pudełko."},"package":{"type":"integer","nullable":true,"example":1,"description":"Opakowanie (najmniejsze)."}}},"unit":{"type":"string","example":"opak","description":"Jednostka, w której podane są ilości, ceny i stany (np. „szt”, „opak”)."},"priceNetto":{"type":"number","example":10.95,"description":"Cena katalogowa netto za jednostkę (przed rabatem), PLN."},"yourPriceNetto":{"type":"number","nullable":true,"example":7.67,"description":"Twoja aktualna cena netto (po rabacie konta, promocji, wyprzedaży lub ofercie) — informacyjnie. Po tej cenie zostanie złożone zamówienie, jeśli nie zmieni się do chwili jego złożenia (promocje i oferty mogą ją zmienić bez zmiany `updatedAt`); aktualną sprawdzisz przez `POST /v1/orders/validate`. Null, gdy konto nie ma włączonych zamówień, produkt nie jest dostępny w sprzedaży przez API (wtedy `availableStock` wynosi 0) albo warunki konta są chwilowo niedostępne (wtedy `availableStock` i `orderStep` są orientacyjne — sprawdź koszyk przez `POST /v1/orders/validate`)."},"orderStep":{"type":"integer","minimum":1,"example":1,"description":"Liczba jednostek w najmniejszym zdefiniowanym opakowaniu; zamawiana ilość musi być jej wielokrotnością. 1 = bez ograniczeń."},"minOrderQuantity":{"type":"integer","minimum":1,"example":1,"description":"Najmniejsza ilość do zamówienia. Zwykle równa `orderStep`; w kategorii Kwiaty sztuczne / Główki kwiatowe — 5 opakowań (5 × `packaging.package`), także dla produktów z pudełkiem. Mniejsza ilość w zamówieniu daje błąd `BELOW_MINIMUM_QUANTITY`."},"availableStock":{"type":"integer","minimum":0,"example":240,"description":"Dostępny stan (zamaskowany). Stany **poniżej 5** zwracamy jako 0; stany **powyżej 2× `outerCarton`** ograniczamy do 2× `outerCarton` (dla produktów bez zdefiniowanego kartonu zbiorczego stan nie jest ograniczany); pozostałe wartości — faktyczny dostępny stan. Dla kont z zamówieniami dodatkowo zaokrąglony w dół do wielokrotności `orderStep` (np. 10 przy kroku 6 → 6) — to maksymalna ilość do zamówienia; produkty niedostępne w sprzedaży przez API mają 0."},"cartonVolume":{"type":"number","nullable":true,"example":0.106,"description":"Objętość kartonu zbiorczego (`outerCarton`) w m³. Null, jeśli nieznana."},"imageUrl":{"type":"string","format":"uri","nullable":true,"example":"https://b2b.akces.pl/api/images/A02/A02-003-01.jpg","description":"Główne zdjęcie wariantu (pierwszy element `images`). Null, jeśli brak zdjęć. Pole zachowane dla zgodności — nowe integracje powinny używać `images`."},"images":{"type":"array","items":{"type":"string","format":"uri"},"example":["https://b2b.akces.pl/api/images/A02/A02-003-01.jpg","https://b2b.akces.pl/api/images/A02/A02-003-02.jpg"],"description":"Wszystkie zdjęcia wariantu jako pełne adresy URL: główne zdjęcie pierwsze, dalej w kolejności galerii. Pusta tablica, jeśli wariant nie ma zdjęć. URL może zawierać parametr wersji (`?v=…`) — zmienia się, gdy plik zostanie podmieniony."},"updatedAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-09-25T14:30:00.000Z","description":"Chwila ostatniej zmiany danych wariantu widocznych w API: dane kartoteki, stan magazynowy, cena katalogowa, zdjęcia. Nie obejmuje zmian `yourPriceNetto` (promocje, oferty, rabaty konta) ani rezerwacji. Null, jeśli nieznana."}}},"Pagination":{"type":"object","required":["total","page","limit","pages"],"properties":{"total":{"type":"integer","example":1,"description":"Liczba wszystkich elementów spełniających filtry."},"page":{"type":"integer","example":1,"description":"Numer bieżącej strony."},"limit":{"type":"integer","example":100,"description":"Liczba elementów na stronie."},"pages":{"type":"integer","example":1,"description":"Liczba stron."}}},"ProductsListResponse":{"type":"object","required":["data","pagination","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"timestamp":{"type":"string","format":"date-time","description":"Czas odpowiedzi — podawaj go (minus 5 min) jako `updatedSince` w kolejnym żądaniu o zmiany."}}},"ProductSingleResponse":{"type":"object","required":["data","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/Product"},"timestamp":{"type":"string","format":"date-time"}}},"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string","example":"Missing API key. Provide X-Partner-Key header.","description":"Opis błędu (po angielsku)."},"code":{"type":"string","example":"UNAUTHORIZED","description":"Kod błędu do obsługi w programie, np. UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED."},"details":{"type":"array","description":"Tylko przy VALIDATION_ERROR: lista niezgodności ze schematem (pole i opis).","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string","example":"lines.0.quantity"},"message":{"type":"string","example":"Expected integer, received float"}}}},"retryAfterSeconds":{"type":"integer","example":60,"description":"Przy 429 (RATE_LIMITED, DAILY_LIMIT_EXCEEDED): liczba sekund do ponowienia (to samo co nagłówek `Retry-After`). Przy 503 pole może nie wystąpić — korzystaj z nagłówka `Retry-After`."},"timestamp":{"type":"string","format":"date-time","description":"Czas odpowiedzi (w odpowiedziach endpointów zamówień)."}}},"OrderRequestLine":{"type":"object","additionalProperties":false,"required":["sku","quantity"],"properties":{"lineNo":{"type":"integer","minimum":1,"maximum":100000,"nullable":true,"example":1,"description":"Numer pozycji w Twoim systemie — wraca w błędach. Domyślnie kolejny numer pozycji w tablicy (od 1)."},"sku":{"type":"string","minLength":1,"maxLength":64,"example":"A02-003","description":"SKU wariantu z katalogu (`GET /v1/products`)."},"ean":{"type":"string","pattern":"^\\d{8,14}$","nullable":true,"example":"5907489847698","description":"Opcjonalnie: EAN (8–14 cyfr) do kontroli krzyżowej mapowania produktu. Niezgodny EAN odrzuca całe zamówienie (EAN_MISMATCH)."},"quantity":{"type":"integer","minimum":1,"maximum":999999,"example":12,"description":"Ilość w jednostce produktu (`unit`): wielokrotność `orderStep`, co najmniej `minOrderQuantity`, najwyżej `availableStock`."},"unitPriceNetto":{"type":"number","nullable":true,"minimum":0,"maximum":1000000,"example":7.67,"description":"Opcjonalnie, tylko informacyjnie — cena netto z Twojego systemu. Nie jest porównywana z ceną Akces; obowiązuje cena Akces."}}},"OrderRequest":{"type":"object","additionalProperties":false,"required":["externalOrderNumber","currency","lines"],"description":"Treść zamówienia (JSON w UTF-8, maks. 256 KB). Nieznane pola są błędem (400 VALIDATION_ERROR) — chroni to przed literówkami w nazwach pól. Pola opcjonalne możesz pominąć albo wysłać jako null.","properties":{"externalOrderNumber":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9/._-]{0,49}$","maxLength":50,"example":"FK/2026/000123","description":"Numer zamówienia w Twoim systemie: 1–50 znaków — litery łacińskie bez polskich znaków, cyfry oraz / . _ -; pierwszy znak to litera lub cyfra; spacje na brzegach są usuwane. Klucz idempotencji: numer jest blokowany przez zamówienia ACCEPTED i PENDING_REVIEW; po REJECTED i DECLINED można go użyć ponownie."},"currency":{"type":"string","enum":["PLN"],"example":"PLN","description":"Waluta zamówienia — zawsze PLN."},"notes":{"type":"string","maxLength":1000,"nullable":true,"example":"Odbiór we wtorek rano","description":"Uwagi do zamówienia (do 1000 znaków) — trafiają na zamówienie w systemie Akces."},"contact":{"type":"object","additionalProperties":false,"nullable":true,"description":"Osoba kontaktowa w sprawie zamówienia lub odbioru.","properties":{"name":{"type":"string","maxLength":120,"nullable":true,"example":"Jan Nowak"},"phone":{"type":"string","maxLength":40,"nullable":true,"example":"+48 600 100 200"},"email":{"type":"string","format":"email","maxLength":200,"nullable":true,"example":"magazyn@example.com"}}},"expectedTotalNetto":{"type":"number","nullable":true,"minimum":0,"maximum":1000000,"example":164.04,"description":"Opcjonalnie, tylko informacyjnie: wartość zamówienia netto w Twoim systemie. Nie jest porównywana z ceną Akces."},"lines":{"type":"array","minItems":1,"maxItems":500,"description":"Pozycje zamówienia (od 1 do 500); każdy SKU najwyżej raz.","items":{"$ref":"#/components/schemas/OrderRequestLine"}}}},"OrderError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["DUPLICATE_LINE","UNKNOWN_SKU","EAN_MISMATCH","PRODUCT_NOT_AVAILABLE","PACKAGING_MULTIPLE","BELOW_MINIMUM_QUANTITY","INSUFFICIENT_STOCK","CONTRACTOR_BLOCKED"],"example":"PACKAGING_MULTIPLE","description":"`DUPLICATE_LINE` — ten sam SKU w kilku pozycjach; `UNKNOWN_SKU` — SKU nie istnieje w katalogu Akces; `EAN_MISMATCH` — podany EAN nie należy do SKU; `PRODUCT_NOT_AVAILABLE` — produkt niedostępny w sprzedaży przez API, np. bez ceny katalogowej albo zablokowany (nie występuje w `GET /v1/products` albo ma tam `yourPriceNetto` null i `availableStock` 0; mógł zniknąć od ostatniego pobrania); sam brak stanu to `INSUFFICIENT_STOCK` z `maxOrderable` 0; `PACKAGING_MULTIPLE` — ilość nie jest wielokrotnością `orderStep`; `BELOW_MINIMUM_QUANTITY` — ilość mniejsza niż `minOrderQuantity`; `INSUFFICIENT_STOCK` — ilość przekracza `availableStock`; `CONTRACTOR_BLOCKED` — konto chwilowo nie może składać zamówień (błąd całego zamówienia, bez `lineNo` i `sku`)."},"message":{"type":"string","example":"Quantity 100 of B14-01 is not a multiple of the packaging unit (96).","description":"Opis błędu (po angielsku)."},"lineNo":{"type":"integer","example":2,"description":"Numer pozycji, której dotyczy błąd. Brak przy błędach całego zamówienia."},"sku":{"type":"string","example":"B14-01","description":"SKU pozycji, której dotyczy błąd. Brak przy błędach całego zamówienia."},"details":{"type":"object","description":"Szczegóły zależne od kodu: PACKAGING_MULTIPLE — `orderStep`, `packaging`, `suggestedQuantities`; BELOW_MINIMUM_QUANTITY — `minOrderQuantity`, `orderStep`; INSUFFICIENT_STOCK — `maxOrderable`; EAN_MISMATCH — `sentEan`.","properties":{"minOrderQuantity":{"type":"integer","example":60,"description":"BELOW_MINIMUM_QUANTITY: najmniejsza ilość do zamówienia."},"orderStep":{"type":"integer","example":96,"description":"PACKAGING_MULTIPLE: krok zamawiania produktu."},"packaging":{"type":"object","description":"PACKAGING_MULTIPLE: pakowanie produktu (jak `Product.packaging`).","properties":{"package":{"type":"integer","nullable":true,"example":null},"innerCarton":{"type":"integer","nullable":true,"example":96},"outerCarton":{"type":"integer","nullable":true,"example":1152}}},"suggestedQuantities":{"type":"array","items":{"type":"integer"},"example":[96,192],"description":"PACKAGING_MULTIPLE: najbliższe poprawne ilości (w dół i w górę)."},"maxOrderable":{"type":"integer","example":240,"description":"INSUFFICIENT_STOCK: ilość, którą możesz zamówić."},"sentEan":{"type":"string","example":"5901234123457","description":"EAN_MISMATCH: przesłany EAN."}}}}},"OrderLine":{"type":"object","description":"Pozycja zamówienia z cenami Akces zastosowanymi w zamówieniu.","required":["lineNo","sku","quantity","unitPriceNetto","basePriceNetto","discountPercent","discountSource","lineTotalNetto"],"properties":{"lineNo":{"type":"integer","example":1,"description":"Numer pozycji (`lineNo` z żądania albo kolejny numer od 1)."},"sku":{"type":"string","example":"A02-003"},"quantity":{"type":"integer","example":12},"unitPriceNetto":{"type":"number","nullable":true,"example":7.67,"description":"Cena Akces zastosowana w zamówieniu (po rabacie), netto za jednostkę. Null dla pozycji odrzuconej przed wyceną (UNKNOWN_SKU, PRODUCT_NOT_AVAILABLE, DUPLICATE_LINE); `lineTotalNetto` wynosi wtedy 0."},"basePriceNetto":{"type":"number","nullable":true,"example":10.95,"description":"Cena katalogowa netto przed rabatem. Null, gdy SKU nie istnieje w katalogu albo nie ma ceny katalogowej."},"discountPercent":{"type":"number","nullable":true,"example":30,"description":"Zastosowany rabat w procentach. Null dla pozycji niewycenionej (jak `unitPriceNetto`)."},"discountSource":{"type":"string","nullable":true,"enum":["company","promotion","offer",null],"example":"company","description":"Źródło rabatu: `company` — stały rabat konta, `promotion` — promocja lub wyprzedaż, `offer` — oferta. Null dla pozycji niewycenionej."},"lineTotalNetto":{"type":"number","example":92.04,"description":"Wartość pozycji netto (ilość × `unitPriceNetto`); 0 dla pozycji niewycenionej."}}},"Order":{"type":"object","required":["id","externalOrderNumber","status","erpOrderNumber","akcesOrderNumber","fulfillmentStatus","currency","totalNetto","lines","errors","declineReason","createdAt","updatedAt"],"properties":{"id":{"type":"string","example":"cmf2x9k0a0001abcd1234efgh","description":"Identyfikator zamówienia w API — do `GET /v1/orders/{id}`."},"externalOrderNumber":{"type":"string","example":"FK/2026/000123","description":"Numer zamówienia w Twoim systemie."},"status":{"type":"string","enum":["ACCEPTED","PENDING_REVIEW","REJECTED","DECLINED"],"example":"ACCEPTED","description":"`ACCEPTED` — przyjęte do realizacji; `PENDING_REVIEW` — złożone i poprawne, czeka na zatwierdzenie przez Akces (towar zarezerwowany); `REJECTED` — odrzucone przez reguły walidacji (`errors`), towar nie był rezerwowany; `DECLINED` — odrzucone przez Akces po weryfikacji (`declineReason`), rezerwacja zwolniona."},"erpOrderNumber":{"type":"string","nullable":true,"example":"ZO/0123/09/26","description":"Numer zamówienia w systemie Akces (ZO) — pojawia się po skierowaniu zamówienia do realizacji."},"akcesOrderNumber":{"type":"string","nullable":true,"example":"CX/0412/09/26","description":"Numer referencyjny zamówienia w Akces, nadawany przy zapisie (także przy odpowiedzi 202), np. CX/0412/09/26 — podawaj go w kontakcie z działem obsługi klienta. Null, gdy zamówienie nie zostało zapisane (REJECTED). `erpOrderNumber` (numer ZO) pojawia się później, po skierowaniu do realizacji."},"fulfillmentStatus":{"type":"string","nullable":true,"example":"DO_REALIZACJI","description":"Etap realizacji zamówienia ACCEPTED: `NOWE` — przyjęte, czeka na skierowanie do realizacji; `DO_REALIZACJI` — czeka na kompletację; `W_REALIZACJI` — kompletowanie, kontrola lub inny etap realizacji; `WSTRZYMANE` — realizacja wstrzymana; `DO_ODBIORU` — gotowe do odbioru w magazynie Akces; `WYSLANE` — towar wydany lub wysłany; `ODEBRANE` — towar odebrany; `CZESCIOWO_ZREALIZOWANE` — zrealizowane częściowo; `ZREALIZOWANE` — zrealizowane w całości; `ANULOWANE` — anulowane (także gdy zamówienie zostało usunięte w systemie Akces przed zakończeniem realizacji). Lista może zostać rozszerzona — nieznaną wartość traktuj jak `W_REALIZACJI`. Null, gdy zamówienie nie jest ACCEPTED (PENDING_REVIEW, REJECTED, DECLINED)."},"currency":{"type":"string","example":"PLN","description":"Zawsze PLN."},"totalNetto":{"type":"number","example":164.04,"description":"Wartość netto zamówienia po cenach Akces. Dla REJECTED informacyjnie: suma wycenionych pozycji (także tych z błędem pakowania, stanu lub EAN); pozycje UNKNOWN_SKU, PRODUCT_NOT_AVAILABLE i DUPLICATE_LINE liczone jako 0."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/OrderLine"}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/OrderError"},"description":"Błędy walidacji — tylko dla REJECTED; w pozostałych statusach pusta tablica."},"declineReason":{"type":"string","nullable":true,"description":"Powód odrzucenia przez Akces — tylko dla DECLINED."},"createdAt":{"type":"string","format":"date-time","example":"2026-09-24T08:15:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2026-09-24T08:15:00.000Z","description":"Ostatnia zmiana zamówienia lub jego realizacji."}}},"OrderResponse":{"type":"object","required":["data","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/Order"},"error":{"type":"string","description":"Tylko przy 409 i 422: opis błędu."},"code":{"type":"string","example":"ORDER_REJECTED","description":"Tylko przy 409 (DUPLICATE_ORDER_NUMBER) i 422 (ORDER_REJECTED)."},"idempotentReplay":{"type":"boolean","example":true,"description":"`true` przy powtórzonym żądaniu (HTTP 200); w pozostałych odpowiedziach pole nie występuje."},"timestamp":{"type":"string","format":"date-time"}}},"OrderSingleResponse":{"type":"object","required":["data","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/Order"},"timestamp":{"type":"string","format":"date-time"}}},"OrdersListResponse":{"type":"object","required":["data","pagination","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"timestamp":{"type":"string","format":"date-time"}}},"OrderValidationLine":{"type":"object","description":"Pozycja walidacji próbnej — ceny, które zostałyby zastosowane, gdybyś złożył zamówienie w tej chwili.","required":["lineNo","sku","quantity","unitPriceNetto","basePriceNetto","discountPercent","discountSource","lineTotalNetto","orderStep","valid"],"properties":{"lineNo":{"type":"integer","example":1,"description":"Numer pozycji (`lineNo` z żądania albo kolejny numer od 1)."},"sku":{"type":"string","example":"A02-003"},"quantity":{"type":"integer","example":12},"unitPriceNetto":{"type":"number","nullable":true,"example":7.67,"description":"Cena Akces zastosowana w zamówieniu (po rabacie), netto za jednostkę. Null dla pozycji odrzuconej przed wyceną (UNKNOWN_SKU, PRODUCT_NOT_AVAILABLE, DUPLICATE_LINE); `lineTotalNetto` wynosi wtedy 0."},"basePriceNetto":{"type":"number","nullable":true,"example":10.95,"description":"Cena katalogowa netto przed rabatem. Null, gdy SKU nie istnieje w katalogu albo nie ma ceny katalogowej."},"discountPercent":{"type":"number","nullable":true,"example":30,"description":"Zastosowany rabat w procentach. Null dla pozycji niewycenionej (jak `unitPriceNetto`)."},"discountSource":{"type":"string","nullable":true,"enum":["company","promotion","offer",null],"example":"company","description":"Źródło rabatu: `company` — stały rabat konta, `promotion` — promocja lub wyprzedaż, `offer` — oferta. Null dla pozycji niewycenionej."},"lineTotalNetto":{"type":"number","example":92.04,"description":"Wartość pozycji netto (ilość × `unitPriceNetto`); 0 dla pozycji niewycenionej."},"orderStep":{"type":"integer","nullable":true,"example":1,"description":"Krok zamawiania produktu. Null dla pozycji odrzuconej przed sprawdzeniem pakowania (UNKNOWN_SKU, PRODUCT_NOT_AVAILABLE, DUPLICATE_LINE)."},"valid":{"type":"boolean","example":true,"description":"Czy pozycja przechodzi walidację."}}},"OrderValidation":{"type":"object","required":["valid","currency","totalNetto","lines","errors"],"properties":{"valid":{"type":"boolean","example":true,"description":"`true` — zamówienie przeszłoby walidację w tej chwili."},"currency":{"type":"string","example":"PLN"},"totalNetto":{"type":"number","example":164.04,"description":"Wartość netto po cenach, które zostałyby zastosowane (pozycje niewycenione liczone jako 0)."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/OrderValidationLine"}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/OrderError"}}}},"OrderValidationResponse":{"type":"object","required":["data","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrderValidation"},"timestamp":{"type":"string","format":"date-time"}}},"Health":{"type":"object","required":["status","service","version","checks","timestamp"],"properties":{"status":{"type":"string","enum":["ok","degraded","error"],"example":"ok","description":"`ok` — wszystko działa; `degraded` — katalog działa, zamówienia chwilowo niedostępne; `error` — katalog nie działa."},"service":{"type":"string","example":"akces-partner-api"},"version":{"type":"string","example":"v1","description":"Wersja główna API."},"checks":{"type":"object","required":["catalog","orders"],"properties":{"catalog":{"type":"string","enum":["ok","error"],"example":"ok","description":"Katalog produktów (baza danych)."},"orders":{"type":"string","enum":["ok","error"],"example":"ok","description":"Obsługa zamówień."}}},"timestamp":{"type":"string","format":"date-time","example":"2026-09-26T08:00:00.000Z"}}},"ApiInfo":{"type":"object","required":["name","version","docs","openapi"],"properties":{"name":{"type":"string","example":"Akces Partner API"},"version":{"type":"string","example":"v1"},"docs":{"type":"string","format":"uri","example":"https://api.akces.pl/docs"},"openapi":{"type":"string","format":"uri","example":"https://api.akces.pl/openapi.json"}}}},"responses":{"Unauthorized":{"description":"Brak lub nieprawidłowy klucz API (UNAUTHORIZED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Adres IP spoza listy dozwolonych (FORBIDDEN) albo żądanie przez http:// zamiast https:// (HTTPS_REQUIRED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Nie znaleziono zasobu (NOT_FOUND), np. produktu o podanym SKU w katalogu API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Przekroczony limit żądań na minutę (RATE_LIMITED) albo — tylko `POST /v1/orders` — limit prób zamówień w oknie 24 h (DAILY_LIMIT_EXCEEDED). Odczekaj co najmniej tyle sekund, ile podaje nagłówek `Retry-After` (to samo w polu `retryAfterSeconds`).","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Liczba sekund do ponowienia."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Błąd po stronie Akces (INTERNAL_ERROR) — ponów później.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServiceUnavailable":{"description":"Usługa chwilowo niedostępna (SERVICE_UNAVAILABLE) — ponów po czasie z nagłówka `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Liczba sekund do ponowienia."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}