Wszystko, czego potrzebuje Twój zespół IT i zakupów, żeby wysyłać do Akces gotowe zamówienia: zasady współpracy, bezpieczeństwo, format danych, reguły walidacji, statusy i przykłady kodu.
Zmiany w API opisuje historia zmian.
Pięć kroków od katalogu do odbioru towaru. Bazowy adres API: https://api.akces.pl.
GET /v1/products. Dla każdego produktu dostajesz m.in. yourPriceNetto (Twoja cena — informacyjnie), orderStep (krok ilości wynikający z pakowania), minOrderQuantity (najmniejsza ilość) i availableStock (dostępność). Pełną listę odświeżaj co 15–30 minut (sekcja 5).orderStep, nie mniejsza niż minOrderQuantity. Ceny nie musisz podawać — zawsze obowiązuje cena Akces.POST /v1/orders/validate. Te same reguły co przy zamówieniu (produkty, pakowanie, dostępność, ceny), ale bez rezerwacji towaru i bez zapisu. W odpowiedzi dostajesz ceny, które zostałyby zastosowane, gdybyś złożył zamówienie w tej chwili, i listę ewentualnych błędów.POST /v1/orders z podpisem HMAC. Odpowiedź przychodzi od razu: przyjęte (ACCEPTED), czeka na zatwierdzenie (PENDING_REVIEW) albo odrzucone (REJECTED) z pełną listą błędów.GET /v1/orders?updatedSince=…: numer zamówienia w systemie Akces, etap realizacji, gotowość do odbioru.Reguły handlowe, które obowiązują każde zamówienie złożone przez API.
| Temat | Zasada |
|---|---|
| Konto | Zamówienie jest zawsze składane na firmę przypisaną do Twojego klucza API. W zamówieniu nie podajesz NIP-u ani danych firmy — nie da się złożyć zamówienia na inne konto. |
| Płatność i dokument | Płatność przelewem, termin zgodnie z Twoimi warunkami handlowymi w Akces. Dokument sprzedaży: faktura VAT. |
| Ceny | Zawsze obowiązuje cena Akces z chwili złożenia zamówienia (odpowiedź 201/202): cena katalogowa netto w PLN za jednostkę produktu (unit) z najkorzystniejszym dla Ciebie rabatem. Cena podana w zamówieniu (unitPriceNetto) nie jest wymagana ani porównywana z ceną Akces — zapisujemy ją wyłącznie informacyjnie. Ceny zastosowane w zamówieniu zwracamy w odpowiedzi. VAT doliczamy na fakturze. |
| Rabaty | Naliczamy automatycznie najkorzystniejszy rabat: stały rabat Twojego konta, aktywną promocję lub wyprzedaż albo ofertę przygotowaną dla Twojej firmy. Rabaty nie sumują się — obowiązuje najniższa cena. W zamówieniu nie podajesz rabatu. Aktualną cenę widzisz w katalogu jako yourPriceNetto. |
| Ilości i pakowanie | Zamawiana ilość musi być wielokrotnością najmniejszego opakowania produktu (orderStep) i nie mniejsza niż minOrderQuantity. Nie przyjmujemy ilości, które wymagałyby otwierania (dzielenia) opakowań. W kategorii Kwiaty sztuczne / Główki kwiatowe obowiązuje minimum 5 opakowań w kolorze — także dla produktów z pudełkiem (pudełko lub karton wystarcza, jeśli mieści co najmniej 5 opakowań). |
| Dostępność | Możesz zamówić najwyżej tyle, ile wynosi availableStock w chwili złożenia zamówienia — wartość z wcześniej pobranego katalogu mogła się w międzyczasie zmienić. Większe ilości uzgodnij z działem obsługi klienta. Towar rezerwujemy w chwili złożenia zamówienia — nie trafi już do innych zamówień składanych w sklepie B2B ani przez API. W wyjątkowych sytuacjach (np. sprzedaż bezpośrednio w magazynie) towaru może zabraknąć przed realizacją — wtedy dział obsługi klienta skontaktuje się z Tobą. |
| Całość albo nic | Jeśli choć jedna pozycja jest błędna, odrzucamy całe zamówienie i zwracamy listę wszystkich błędów. Nie zapisujemy zamówienia z pominięciem błędnych pozycji. |
| Odbiór | Zamówienia są przygotowywane do odbioru osobistego: Magazyn Akces, ul. Sławęcińska 3, 05-850 Macierzysz. API nie przyjmuje adresu wysyłki. Gotowość do odbioru sygnalizuje status realizacji DO_ODBIORU. |
| Zatwierdzanie | W okresie startowym każde poprawne zamówienie przed skierowaniem do realizacji zatwierdza dział obsługi klienta Akces (status PENDING_REVIEW). Towar jest zarezerwowany już od złożenia zamówienia. Zatwierdzenie przez Akces (PENDING_REVIEW → ACCEPTED) nie zmienia cen ani rezerwacji. Status PENDING_REVIEW nie wygasa automatycznie; towar pozostaje zarezerwowany do decyzji Akces. Jeśli zamówienie zostanie odrzucone przez Akces, dostaniesz status DECLINED z podanym powodem. |
| Numer zamówienia | Każde zamówienie ma numer z Twojego systemu (externalOrderNumber). Służy do bezpiecznego ponawiania i pojawia się na dokumentach po stronie Akces. |
| Zmiany i anulowanie | API nie zmienia ani nie anuluje złożonych zamówień. Zmianę lub anulowanie zgłoś do działu obsługi klienta, podając swój numer zamówienia (externalOrderNumber) albo numer Akces: akcesOrderNumber (np. CX/0412/09/26) lub erpOrderNumber (numer zamówienia sprzedaży, ZO, np. ZO/0123/09/26). |
Składanie zamówień wymaga trzech zabezpieczeń naraz. Wszystkie dane dostępowe przekazujemy osobnymi kanałami.
| Element | Gdzie | Opis |
|---|---|---|
| Klucz API | nagłówek X-Partner-Key | Format pk_ + 32 znaki hex. Wymagany w każdym żądaniu (katalog i zamówienia). Identyfikuje Twoją firmę. |
| Sekret podpisu | tylko do obliczenia X-Signature | Format whsec_…. Służy do podpisania każdego żądania zamówienia (sekcja 4). Nigdy nie wysyłaj sekretu w żądaniu. |
| Lista dozwolonych adresów IP | po stronie Akces | Przyjmujemy wszystkie żądania z Twoim kluczem (katalog i zamówienia) tylko z uzgodnionych adresów IP Twoich serwerów. Żądanie z innego adresu dostaje 403 FORBIDDEN. Zmianę adresów zgłoś z wyprzedzeniem na kontakt@akces.pl. |
Origin). Klucza i sekretu nie umieszczaj w kodzie front-endu, aplikacji mobilnej ani w repozytorium.https://api.akces.pl. Żądanie do /v1 przez http:// dostaje 403 HTTPS_REQUIRED.X-Partner-Key i X-Signature w pełnej postaci.Wymagany dla POST /v1/orders i POST /v1/orders/validate. Dzięki podpisowi sam wyciek klucza nie pozwala złożyć zamówienia, a przechwyconego żądania nie da się użyć po upływie 5 minut (okno X-Timestamp); w tym oknie powtórka nie utworzy duplikatu dzięki idempotencji po externalOrderNumber.
1790172000. Wyślesz go w nagłówku X-Timestamp.<X-Timestamp>.<ścieżka>.<body>, gdzie ścieżka to /v1/orders albo /v1/orders/validate (bez domeny i parametrów).X-Signature: sha256=<wynik>.X-Partner-Key: pk_… X-Timestamp: 1790172000 X-Signature: sha256=hex( HMAC_SHA256( sekret, "1790172000" + "." + "/v1/orders" + "." + body ) ) Content-Type: application/json
Okno czasowe: znacznik czasu może różnić się od naszego zegara najwyżej o 300 sekund. Zsynchronizuj zegar serwera przez NTP. Przy ponowieniu żądania policz nowy znacznik czasu i nowy podpis.
Ścieżka w podpisie powoduje, że podpisu walidacji próbnej nie da się użyć do złożenia prawdziwego zamówienia.
Sprawdź swoją implementację na tych danych — musisz otrzymać dokładnie podany podpis.
| Dane | Wartość |
|---|---|
| Sekret | whsec_przyklad_do_testow |
| X-Timestamp | 1790172000 |
| Ścieżka | /v1/orders/validate |
| Body | {"externalOrderNumber":"TEST-0001","currency":"PLN","lines":[{"lineNo":1,"sku":"A02-003","quantity":12}]} |
| Tekst do podpisu | 1790172000./v1/orders/validate.{"externalOrderNumber":"TEST-0001","currency":"PLN","lines":[{"lineNo":1,"sku":"A02-003","quantity":12}]} |
| X-Signature | sha256=2b006a5388444a9866f730c8e7ac22e05d19b908a58055f4970a67e78f845c37 |
# bash + openssl — walidacja próbna BODY='{"externalOrderNumber":"TEST-0001","currency":"PLN","lines":[{"lineNo":1,"sku":"A02-003","quantity":12}]}' TS=$(date +%s) SIG=$(printf '%s' "$TS./v1/orders/validate.$BODY" | openssl dgst -sha256 -hmac "$AKCES_SIGNING_SECRET" | sed 's/^.* //') curl -sS -X POST "https://api.akces.pl/v1/orders/validate" \ -H "Content-Type: application/json" \ -H "X-Partner-Key: $AKCES_API_KEY" \ -H "X-Timestamp: $TS" \ -H "X-Signature: sha256=$SIG" \ --data-binary "$BODY" # --data-binary: wysyła dokładnie te bajty
// Node.js 18+ (fetch wbudowany) import crypto from "node:crypto"; async function sendOrder(order, path = "/v1/orders") { const body = JSON.stringify(order); // ten sam tekst podpisujemy i wysyłamy const ts = String(Math.floor(Date.now() / 1000)); const signature = crypto .createHmac("sha256", process.env.AKCES_SIGNING_SECRET) .update(ts + "." + path + "." + body, "utf8") .digest("hex"); const res = await fetch("https://api.akces.pl" + path, { method: "POST", headers: { "Content-Type": "application/json", "X-Partner-Key": process.env.AKCES_API_KEY, "X-Timestamp": ts, "X-Signature": "sha256=" + signature, }, body, }); return { status: res.status, json: await res.json() }; }
// PHP 8+ z rozszerzeniem curl function sendOrder(array $order, string $path = "/v1/orders"): array { $body = json_encode($order, JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION); $ts = (string) time(); $signature = hash_hmac("sha256", $ts . "." . $path . "." . $body, getenv("AKCES_SIGNING_SECRET")); $ch = curl_init("https://api.akces.pl" . $path); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, // dokładnie ten sam tekst, który podpisano CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 90, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "X-Partner-Key: " . getenv("AKCES_API_KEY"), "X-Timestamp: " . $ts, "X-Signature: sha256=" . $signature, ], ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return ["status" => $status, "json" => json_decode($response, true)]; }
# Python 3 + requests import hashlib, hmac, json, os, time, requests def send_order(order, path="/v1/orders"): body = json.dumps(order, ensure_ascii=False, separators=(",", ":")).encode("utf-8") ts = str(int(time.time())) message = ts.encode() + b"." + path.encode() + b"." + body signature = hmac.new(os.environ["AKCES_SIGNING_SECRET"].encode(), message, hashlib.sha256).hexdigest() r = requests.post("https://api.akces.pl" + path, data=body, timeout=90, headers={ "Content-Type": "application/json", "X-Partner-Key": os.environ["AKCES_API_KEY"], "X-Timestamp": ts, "X-Signature": "sha256=" + signature, }) return r.status_code, r.json()
// .NET 6+ (System.Text.Json, HttpClient) using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; // Nazwy pól jak w API (camelCase), puste pola opcjonalne pomijamy static readonly JsonSerializerOptions AkcesJson = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; static async Task<HttpResponseMessage> SendOrder(HttpClient http, object order, string path = "/v1/orders") { var body = JsonSerializer.Serialize(order, AkcesJson); var ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("AKCES_SIGNING_SECRET")!)); var signature = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(ts + "." + path + "." + body))).ToLowerInvariant(); var request = new HttpRequestMessage(HttpMethod.Post, "https://api.akces.pl" + path) { Content = new StringContent(body, Encoding.UTF8, "application/json") }; request.Headers.Add("X-Partner-Key", Environment.GetEnvironmentVariable("AKCES_API_KEY")); request.Headers.Add("X-Timestamp", ts); request.Headers.Add("X-Signature", "sha256=" + signature); return await http.SendAsync(request); }
/v1/orders/validate, a wysyłka na /v1/orders).TIMESTAMP_INVALID) albo rozjechany zegar serwera (TIMESTAMP_EXPIRED).Zamówienie budujesz na podstawie GET /v1/products (przykład z komentarzami: /docs#przyklad-produktu; pełny schemat: Swagger UI). Dla zamówień kluczowe są:
| Pole | Znaczenie dla zamówienia |
|---|---|
sku | Kod produktu (wariantu), który podajesz w pozycji zamówienia. |
ean | Kod kreskowy. Możesz go dołączyć do pozycji jako kontrolę — jeśli nie pasuje do SKU, całe zamówienie zostanie odrzucone (błąd EAN_MISMATCH w tej pozycji). Chroni to przed błędnym mapowaniem produktów po Twojej stronie. |
unit | Jednostka, w której podajesz ilość i w której liczona jest cena (np. szt, opak). Nie przeliczamy jednostek. |
yourPriceNetto | Twoja aktualna cena netto za jednostkę (po rabacie konta / promocji / wyprzedaży / ofercie) — 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 produktu nie można zamówić przez API albo warunki są chwilowo niedostępne — wtedy availableStock i orderStep są orientacyjne; sprawdź koszyk przez POST /v1/orders/validate. |
priceNetto | Cena katalogowa przed rabatem — informacyjnie. |
packaging | Ilości w opakowaniu (package), pudełku (innerCarton) i kartonie zbiorczym (outerCarton), w jednostce produktu. |
orderStep | Liczba jednostek w najmniejszym zdefiniowanym opakowaniu; ilość w zamówieniu musi być jej wielokrotnością. 1 = bez ograniczeń. |
minOrderQuantity | Najmniejsza ilość, jaką możesz zamówić. Zwykle równa orderStep; w Główkach kwiatowych = 5 opakowań (5 × packaging.package), także dla produktów z pudełkiem. |
availableStock | Maksymalna ilość, jaką możesz zamówić (patrz niżej). |
Produkt ma opakowanie po 5 szt., pudełko po 20 szt. i karton po 100 szt., więc orderStep = 5. Możesz zamówić dowolną wielokrotność 5: 5, 10, 15, 20, 25… Zamówienie na 9 szt. zostanie odrzucone z podpowiedzią najbliższych poprawnych ilości (5 i 10). Podobnie B14-01: pudełko po 96 szt. i karton po 1152 szt., więc orderStep = 96.
Główki kwiatowe — minimum 5 opakowań w kolorze: B101-01 (opakowanie 12, pudełko 108, karton 432) ma orderStep = 12 i minOrderQuantity = 60 — możesz zamówić 60, 72, 84… (np. 5 opakowań, całe pudełko 108 albo karton 432), ale nie 24 (błąd BELOW_MINIMUM_QUANTITY). Tak samo D77-01 (opakowanie 12, karton 720): orderStep = 12, minOrderQuantity = 60.
availableStock to maksymalna ilość produktu, jaką możesz zamówić. Stan jest zamaskowany: poniżej 5 jednostek pokazujemy 0, a powyżej dwóch kartonów zbiorczych — 2× outerCarton (dla produktów bez zdefiniowanego kartonu zbiorczego stan nie jest ograniczany do 2× karton). Wynik zaokrąglamy w dół do wielokrotności orderStep (np. 10 przy kroku 6 → 6); jeśli jest mniejszy niż minOrderQuantity, pokazujemy 0. Produkty, których nie można zamówić przez API (np. bez ceny katalogowej albo zablokowane), mają availableStock 0 i yourPriceNetto null.
Zamówienie na większą ilość zostanie odrzucone (INSUFFICIENT_STOCK, w details.maxOrderable podajemy ilość możliwą do zamówienia). Tę samą regułę stosuje walidacja próbna. Jeśli potrzebujesz większej ilości, skontaktuj się z działem obsługi klienta.
Parametr updatedSince i pole updatedAt obejmują zmiany kartoteki, stanów, cen katalogowych i zdjęć. Nie obejmują yourPriceNetto (promocje, oferty, rabat konta) ani rezerwacji wynikających z zamówień innych klientów. Dlatego:
GET /v1/products?limit=200, wszystkie strony);updatedSince równe polu timestamp z poprzedniej odpowiedzi minus 5 minut (odpowiedź może zawierać produkty bez zmian — przetwarzaj ją idempotentnie);POST /v1/orders/validate.Cenę narzuca Akces: w chwili złożenia zamówienia naliczamy cenę katalogową z najkorzystniejszym rabatem (konto / promocja / wyprzedaż / oferta). Ceny i dostępność zmieniają się na bieżąco (zmiana ceny katalogowej, początek lub koniec promocji albo oferty, zamówienia innych klientów), dlatego yourPriceNetto i availableStock z wcześniej pobranego katalogu mogą się różnić od wartości w chwili złożenia zamówienia. Ceny zastosowane w zamówieniu zawsze zwracamy w odpowiedzi (unitPriceNetto, discountPercent, lineTotalNetto, totalNetto) — zapisz je w swoim systemie. Aktualne ceny i dostępność przed wysłaniem pokaże POST /v1/orders/validate.
POST /v1/orders (i /v1/orders/validate), treść żądania (body) JSON w UTF-8, maks. 256 KB. Nieznane pola są błędem — literówka w nazwie pola nie przejdzie niezauważona.
| Pole | Wymagane | Zasady |
|---|---|---|
externalOrderNumber | tak | 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. Numer jest zablokowany przez zamówienia ACCEPTED i PENDING_REVIEW; po REJECTED i DECLINED można go użyć ponownie. Służy do idempotencji (sekcja 10). |
currency | tak | Zawsze "PLN". |
lines | tak | Pozycje zamówienia: od 1 do 500. Każdy SKU najwyżej raz. |
lines[].sku | tak | Kod produktu z katalogu (1–64 znaki). |
lines[].quantity | tak | Liczba całkowita od 1 do 999 999, wielokrotność orderStep, co najmniej minOrderQuantity, najwyżej availableStock. |
lines[].unitPriceNetto | nie | Liczba (jeśli podana) od 0 do 1 000 000 albo null. Cena netto z Twojego systemu — tylko informacyjnie, nie jest porównywana z ceną Akces. Obowiązuje cena Akces. |
lines[].lineNo | nie | Numer linii w Twoim systemie (liczba całkowita od 1 do 100 000). Wraca w odpowiedzi i w błędach — ułatwia wskazanie pozycji. Bez niego numerujemy pozycje kolejno od 1. |
lines[].ean | nie | 8–14 cyfr. Kontrola krzyżowa z kartoteką produktu. |
notes | nie | Uwagi do zamówienia, do 1000 znaków. Trafiają na zamówienie w systemie Akces. |
contact | nie | Osoba kontaktowa w sprawie zamówienia / odbioru: name (do 120 znaków), phone (do 40 znaków), email (poprawny adres, do 200 znaków). |
expectedTotalNetto | nie | Liczba (jeśli podana) od 0 do 1 000 000 albo null. Wartość zamówienia z Twojego systemu — tylko informacyjnie, nie jest porównywana z wartością Akces. |
Nazwy pól podawaj dokładnie jak w tabeli (camelCase) — nieznane pola dają 400. Puste pola opcjonalne najlepiej pomijaj; wartość null przyjmujemy w polach notes, contact, contact.name, contact.phone, contact.email, lines[].lineNo, lines[].ean, lines[].unitPriceNetto i expectedTotalNetto.
{
"externalOrderNumber": "FK/2026/000123",
"currency": "PLN",
"notes": "Odbiór we wtorek rano",
"contact": { "name": "Jan Nowak", "phone": "+48 600 100 200", "email": "magazyn@example.com" },
"lines": [
{ "lineNo": 1, "sku": "A02-003", "ean": "5907489847698", "quantity": 12 },
{ "lineNo": 2, "sku": "B14-01", "quantity": 96 }
]
}
Sprawdzamy wszystkie reguły naraz i zwracamy pełną listę błędów z numerem linii (lineNo) i SKU. Jeden błąd odrzuca całe zamówienie.
| Kod | Co oznacza | Co zrobić |
|---|---|---|
UNKNOWN_SKU | SKU nie istnieje w katalogu Akces. | Sprawdź mapowanie produktów; odśwież katalog. |
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 daje INSUFFICIENT_STOCK z maxOrderable 0. | Usuń pozycję; odśwież katalog. |
EAN_MISMATCH | Podany EAN nie należy do SKU. W details.sentEan — wysłany EAN. | Popraw mapowanie EAN ↔ SKU po swojej stronie. |
DUPLICATE_LINE | Ten sam SKU występuje w zamówieniu więcej niż raz. | Połącz ilości w jedną pozycję. |
PACKAGING_MULTIPLE | Ilość nie jest wielokrotnością orderStep. W details: orderStep, packaging, suggestedQuantities. | Zaokrąglij ilość do jednej z podpowiedzianych. |
BELOW_MINIMUM_QUANTITY | Ilość jest mniejsza niż minOrderQuantity (minimalne ilości w kolorze — Główki kwiatowe). W details: minOrderQuantity, orderStep. | Zwiększ ilość do co najmniej minOrderQuantity albo usuń pozycję. |
INSUFFICIENT_STOCK | Ilość przekracza availableStock. W details.maxOrderable — ilość, którą możesz zamówić. | Zmniejsz ilość do maxOrderable albo usuń pozycję. |
CONTRACTOR_BLOCKED | Konto chwilowo nie może składać zamówień. | Skontaktuj się z działem obsługi klienta. |
{
"error": "Order rejected. See errors for details.",
"code": "ORDER_REJECTED",
"data": {
"id": "cmf2xa1b20002abcd1234efgh",
"externalOrderNumber": "FK/2026/000124",
"status": "REJECTED",
… // skrócone — pozostałe pola jak w odpowiedzi 202 (sekcja 8); dla REJECTED akcesOrderNumber i fulfillmentStatus mają wartość null
"errors": [
{ "lineNo": 1, "sku": "A02-003", "code": "INSUFFICIENT_STOCK",
"message": "Quantity of A02-003 exceeds the available quantity (availableStock).",
"details": { "maxOrderable": 240 } },
{ "lineNo": 2, "sku": "B14-01", "code": "PACKAGING_MULTIPLE",
"message": "Quantity 100 of B14-01 is not a multiple of the packaging unit (96).",
"details": { "orderStep": 96, "packaging": { "package": null, "innerCarton": 96, "outerCarton": 1152 },
"suggestedQuantities": [96, 192] } }
]
},
"timestamp": "2026-09-24T08:20:00.095Z"
}
Odrzucone zamówienie nie rezerwuje towaru. Popraw błędy i wyślij je ponownie — możesz użyć tego samego externalOrderNumber.
Każda odpowiedź to JSON. Błędy mają pola error (opis) i code (kod do obsługi w programie).
| HTTP | Kod | Znaczenie |
|---|---|---|
| 201 | — | Zamówienie przyjęte do realizacji (status: ACCEPTED), towar zarezerwowany. |
| 202 | — | Zamówienie poprawne, towar zarezerwowany, czeka na zatwierdzenie przez Akces (status: PENDING_REVIEW). |
| 200 | — | Powtórzone żądanie z tym samym numerem i treścią — dostajesz aktualny stan pierwotnego zamówienia (idempotentReplay: true), nowe zamówienie nie powstaje. Walidacja próbna też zwraca 200. |
| 400 | VALIDATION_ERROR, INVALID_JSON | Body niezgodne ze schematem (lista w details: pole i opis), niepoprawny JSON albo niepoprawny parametr zapytania (np. updatedSince). Zamówienie nie jest zapisywane. |
| 401 | UNAUTHORIZED, SIGNATURE_MISSING, SIGNATURE_INVALID, TIMESTAMP_INVALID, TIMESTAMP_EXPIRED | Brak lub zły klucz API, brak lub zły podpis, znacznik czasu niebędący liczbą sekund Unix albo spoza okna ±300 s. |
| 403 | FORBIDDEN, ORDERS_DISABLED, BROWSER_NOT_ALLOWED, HTTPS_REQUIRED | Adres IP spoza listy dozwolonych, zamówienia przez API niewłączone dla konta, żądanie z przeglądarki, żądanie przez http:// zamiast https://. |
| 404 | NOT_FOUND | Zamówienie o podanym id nie istnieje albo nie należy do Twojej firmy (GET /v1/orders/{id}); także nieznany adres pod /v1. |
| 405 | METHOD_NOT_ALLOWED | Metoda HTTP nieobsługiwana pod tym adresem (np. GET /v1/orders/validate). Dozwolone metody podaje nagłówek Allow. |
| 409 | DUPLICATE_ORDER_NUMBER | Ten numer jest już użyty w zamówieniu ACCEPTED lub PENDING_REVIEW, a treść jest inna. |
| 413 | PAYLOAD_TOO_LARGE | Body większe niż 256 KB — podziel zamówienie. |
| 415 | UNSUPPORTED_MEDIA_TYPE | Brak nagłówka Content-Type albo wartość inna niż application/json. |
| 422 | ORDER_REJECTED | Zamówienie odrzucone przez reguły biznesowe — pełna lista w data.errors. |
| 429 | RATE_LIMITED, DAILY_LIMIT_EXCEEDED | Przekroczony limit (sekcja 11). Odczekaj czas z nagłówka Retry-After (przy DAILY_LIMIT_EXCEEDED także w polu retryAfterSeconds). |
| 5xx (500, 502, 503, 504) | INTERNAL_ERROR, SERVICE_UNAVAILABLE | Chwilowy problem po stronie Akces; 503 ma nagłówek Retry-After. Ponów z tym samym numerem zamówienia i treścią (sekcja 10). |
{
"data": {
"id": "cmf2x9k0a0001abcd1234efgh", // zapisz — do GET /v1/orders/{id}
"externalOrderNumber": "FK/2026/000123",
"status": "PENDING_REVIEW",
"erpOrderNumber": null, // numer ZO — po skierowaniu do realizacji
"akcesOrderNumber": "CX/0412/09/26", // numer Akces — nadawany od razu
"fulfillmentStatus": null, // null do czasu przyjęcia (ACCEPTED)
"currency": "PLN",
"totalNetto": 164.04,
"lines": [ // ceny Akces zastosowane w zamówieniu
{ "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"
}
Ceny i rabaty w tym przykładzie są umowne.
id — identyfikator do GET /v1/orders/{id}.akcesOrderNumber — numer referencyjny zamówienia w Akces, nadawany przy zapisie (także przy 202), np. CX/0412/09/26. Podawaj go w kontakcie z działem obsługi klienta.erpOrderNumber — numer zamówienia sprzedaży (ZO, np. ZO/0123/09/26); pojawia się po skierowaniu zamówienia do realizacji.Zamówienie ma dwa poziomy statusu: status (decyzja o przyjęciu) i — dla zamówień przyjętych (ACCEPTED) — fulfillmentStatus (etap realizacji w Akces).
| Wartość | Znaczenie |
|---|---|
PENDING_REVIEW | Zamówienie poprawne, towar zarezerwowany, czeka na zatwierdzenie przez dział obsługi klienta Akces. Status nie wygasa automatycznie. |
ACCEPTED | Zamówienie przyjęte do realizacji. fulfillmentStatus jest ustawiany od razu (NOWE); erpOrderNumber (numer ZO) pojawi się po przekazaniu zamówienia do realizacji. |
REJECTED | Odrzucone automatycznie przez reguły walidacji — lista w errors. Towar nie był rezerwowany. |
DECLINED | Odrzucone przez Akces po weryfikacji — powód w declineReason. Rezerwacja została zwolniona. |
| Wartość | Znaczenie |
|---|---|
NOWE | Zamówienie przyjęte, czeka na przekazanie do realizacji. |
DO_REALIZACJI | Przekazane do realizacji, czeka na kompletację. |
W_REALIZACJI | W trakcie realizacji: kompletowanie, kontrola i inne etapy pośrednie w magazynie. |
WSTRZYMANE | Realizacja wstrzymana — w razie wątpliwości skontaktuj się z działem obsługi klienta. |
DO_ODBIORU | Gotowe do odbioru w magazynie Akces. |
WYSLANE | Towar wydany z magazynu (wysłany). |
ODEBRANE | Towar odebrany. |
CZESCIOWO_ZREALIZOWANE | Zamówienie zrealizowane częściowo. |
ZREALIZOWANE | Zamówienie zrealizowane w całości. |
ANULOWANE | Zamówienie anulowane lub usunięte w systemie Akces (zwykle po uzgodnieniu z Tobą). Zamówienie już zakończone zachowuje swój ostatni status. |
fulfillmentStatus ma wartość null, dopóki zamówienie nie ma statusu ACCEPTED (PENDING_REVIEW, REJECTED, DECLINED). Lista może zostać rozszerzona o kolejne wartości — nieznaną wartość traktuj jak W_REALIZACJI.
GET /v1/orders?updatedSince=2026-09-24T08:00:00Z — zamówienia zmienione od podanej chwili (także statusy realizacji). Parametry: updatedSince (ISO 8601), externalOrderNumber, page, limit (maks. 100, domyślnie 50). Wynik posortowany od najnowszych, z sekcją pagination.GET /v1/orders/{id} — jedno zamówienie (id z odpowiedzi na POST).updatedSince równym polu timestamp z poprzedniej odpowiedzi minus 5 minut zapasu. Odpowiedź może zawierać zamówienia bez zmian — przetwarzaj ją idempotentnie.externalOrderNumber jest kluczem idempotencji — dzięki niemu ponowienie nigdy nie tworzy duplikatu.
| Sytuacja | Co robimy |
|---|---|
Ten sam numer i ta sama treść, zamówienie ACCEPTED lub PENDING_REVIEW | Zwracamy aktualny stan pierwotnego zamówienia (HTTP 200, idempotentReplay: true). Nie powstaje nowe zamówienie. |
Ten sam numer, inna treść, zamówienie ACCEPTED lub PENDING_REVIEW | HTTP 409 DUPLICATE_ORDER_NUMBER. Zmiany w takim zamówieniu — tylko przez dział obsługi klienta. |
Ten sam numer po odrzuceniu (REJECTED / DECLINED) | Traktujemy jak nowe zamówienie — możesz wysłać poprawioną wersję pod tym samym numerem. |
„Ta sama treść” = te same pozycje w tej samej kolejności (sku, quantity, lineNo, ean), te same uwagi (notes), kontakt (contact) i waluta. Ceny informacyjne (unitPriceNetto, expectedTotalNetto) nie są porównywane — ponowienie z inną ceną informacyjną to nadal powtórka (HTTP 200). Przy ponowieniu najlepiej wysyłaj dokładnie ten sam JSON co za pierwszym razem — zapisz go przed wysłaniem.
externalOrderNumber i tą samą treścią, z nowym znacznikiem czasu i podpisem. Po 502/504 i po przekroczeniu czasu zamówienie mogło już zostać złożone — ponowienie zwróci wtedy 200 z idempotentReplay: true. Odstępy: np. 5 s, 30 s, 2 min, 10 min.Retry-After, potem ponawiaj z rosnącymi odstępami (np. 2 min, 10 min) — przy 503 z tym samym externalOrderNumber i treścią.GET /v1/orders?externalOrderNumber=…. Możesz dostać kilka prób z tym numerem (np. odrzuconą i poprawioną); obowiązuje ta ze statusem ACCEPTED lub PENDING_REVIEW.| Operacja | Limit |
|---|---|
POST /v1/orders | 10 żądań na minutę |
POST /v1/orders/validate | 30 żądań na minutę |
GET /v1/orders, GET /v1/orders/{id} | 60 żądań na minutę |
GET /v1/products i GET /v1/products/{sku} (wspólny limit) | Limit ustalony dla konta (domyślnie 60 żądań na minutę) |
| Wielkość zamówienia | Maks. 500 pozycji i 256 KB body |
| Próby zamówień | 500 prób w ciągu ostatnich 24 h (okno kroczące; złożone i odrzucone łącznie) |
| Nieudane uwierzytelnienie (zły lub brakujący klucz) | 30 na minutę z jednego adresu IP |
Po przekroczeniu limitu dostajesz HTTP 429 z nagłówkiem Retry-After (liczba sekund do ponowienia):
RATE_LIMITED — przekroczony limit na minutę; odczekaj czas z Retry-After.DAILY_LIMIT_EXCEEDED — przekroczony limit prób w oknie 24 h. Retry-After i pole retryAfterSeconds w treści odpowiedzi podają, za ile sekund najstarsza próba wypadnie z okna (co najmniej 60 s).Retry-After (60 s).Jeśli limity są za niskie dla Twojej skali, napisz na kontakt@akces.pl.
Nie ma osobnego środowiska testowego — testuj na produkcyjnym katalogu, cenach i stanach przez walidację próbną.
POST /v1/orders/validate przyjmuje to samo body i podpis liczony tym samym algorytmem (ze ścieżką /v1/orders/validate), ale niczego nie zapisuje i nie rezerwuje. Odpowiedź (HTTP 200) w polu data: valid, currency, totalNetto, lines[] (lineNo, sku, quantity, unitPriceNetto, basePriceNetto, discountPercent, discountSource, lineTotalNetto, orderStep, valid) i errors.externalOrderNumber był już użyty (409 lub powtórka 200), ani limitu prób na 24 h.POST /v1/orders/validate przed wysłaniem.orderStep, co najmniej minOrderQuantity, najwyżej availableStock.unitPriceNetto, totalNetto) i numeru akcesOrderNumber.data.valid: true).externalOrderNumber po błędach sieci i 5xx (przy 429 i 503 po odczekaniu Retry-After).GET /v1/orders?updatedSince=…) i reakcja na DO_ODBIORU.Najczęściej podpisany tekst różni się od wysłanego body (ponowna serializacja JSON przez bibliotekę HTTP, inne formatowanie, BOM) albo ścieżka w podpisie nie odpowiada adresowi. Porównaj swoją implementację z wektorem testowym i wysyłaj body jako gotowy tekst.
TIMESTAMP_EXPIRED — zegar serwera różni się od naszego o ponad 300 s: włącz synchronizację NTP. TIMESTAMP_INVALID — X-Timestamp nie jest liczbą sekund Unix (np. wysyłasz milisekundy, 13 cyfr): używaj sekund.
Żądanie wyszło z adresu IP spoza listy dozwolonych adresów (np. nowy serwer, zmiana dostawcy internetu, VPN). Zgłoś nowe adresy na kontakt@akces.pl.
Obowiązuje cena Akces z chwili złożenia zamówienia — w międzyczasie mogła zmienić się cena katalogowa albo zacząć się lub skończyć promocja czy oferta. Ceny zastosowane w zamówieniu są w odpowiedzi (lines[].unitPriceNetto, totalNetto) oraz w GET /v1/orders. Odśwież katalog, żeby mieć aktualne yourPriceNetto, a przed wysłaniem sprawdź koszyk przez POST /v1/orders/validate.
Dostępność zmienia się na bieżąco — inni klienci też zamawiają, a katalog mógł zostać pobrany wcześniej. Zmniejsz ilość do details.maxOrderable albo usuń pozycję. Większe ilości niż availableStock uzgodnij z działem obsługi klienta.
Wyślij je ponownie z tym samym numerem i treścią albo sprawdź GET /v1/orders?externalOrderNumber=…. Duplikat nie powstanie.
Skontaktuj się z działem obsługi klienta, podając externalOrderNumber, akcesOrderNumber albo erpOrderNumber. Wysłanie tego samego numeru z inną treścią zwróci 409.
hurtownia@akces.pl
tel. 22 721 18 30
Odbiór osobisty: Magazyn Akces, ul. Sławęcińska 3, 05-850 Macierzysz.
Podaj externalOrderNumber, akcesOrderNumber albo erpOrderNumber.
W zgłoszeniu podaj: externalOrderNumber lub id zamówienia, czas żądania, kod HTTP i treść odpowiedzi.
Nigdy nie przesyłaj w zgłoszeniu klucza API ani sekretu podpisu.
Specyfikacja techniczna: OpenAPI 3.0 · Swagger UI · Dokumentacja katalogu produktów · Historia zmian.