Pierwsze kroki
Każde żądanie do chronionych endpointów wymaga ważnego tokena JWT w nagłówku. Poniżej trzyetapowy quick-start.
POST /api/token/ z loginem i hasłem. Otrzymasz access (ważny 5 min) oraz refresh.Authorization: Bearer <access_token>POST /api/token/refresh/ z polem refresh, by dostać nowy access bez ponownego logowania.Autoryzacja JWT
API używa JWT (JSON Web Token). Token przekazuj w nagłówku Authorization przy każdym żądaniu.
POST /api/token/ Content-Type: application/json { "username": "twoj_login", "password": "twoje_haslo" }
{ "access": "eyJhbGci...", // <-- używaj tego, wygasa po 5 min "refresh": "eyJhbGci..." // <-- do odświeżenia, ważny 1 dzień }
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
access wygasa po 5 minutach. Odświeżaj go przez POST /api/token/refresh/ z ciałem {"refresh": "..."}.Produkty — przegląd
API udostępnia kilka endpointów produktowych zoptymalizowanych pod różne przypadki użycia: pełne dane, stany magazynowe, aktualizacje i katalogi.
indexid)Pełna lista produktów
Endpoint /api/products-index/ zwraca aktywne produkty ze wszystkimi powiązanymi danymi zagnieżdżonymi w jednej odpowiedzi.
GET /api/products-index/ // lista wszystkich GET /api/products-index/ABC-123/ // jeden produkt po indeksie Authorization: Bearer <token>
Co zawiera jeden produkt:
| Pole | Typ | Opis |
|---|---|---|
index | string | Unikalny kod produktu (klucz w URL) |
quantity | integer | Aktualny stan magazynowy |
active | boolean | Czy produkt jest aktywny |
names[] | array | Nazwy w językach: name_pl, name_en, name_de, name_fr, name_cz |
descriptions[] | array | Opisy wielojęzyczne |
prices[] | array | Ceny: pln, eur, chf, czk |
madeof[] | array | Skład materiałowy (wielojęzyczny) |
image[] | array | Adresy URL zdjęć produktu |
additional[] | array | Atrybuty dodatkowe (klucz–wartość) |
future_delivery[] | array | Planowane dostawy: data + ilość |
marking_data[] | array | Dane znakowania (nested: miejsca, opcje) |
category / subcategory | integer | ID kategorii i podkategorii |
new_product, promotion, sale | boolean | Flagi statusu produktu |
Stany magazynowe
Lekki endpoint zwracający wyłącznie indeks i ilość — idealny do częstej synchronizacji stanów.
GET /api/stock-info/ Authorization: Bearer <token> // Odpowiedź: { "count": 1250, "next": "/api/stock-info/?page=2", "previous": null, "results": [ { "index": "ABC-123", "quantity": 450 }, { "index": "XYZ-007", "quantity": 0 }, ... ] }
/api/last-update/ zwraca produkty zmienione w ostatnich 45 minutach — używaj go do inkrementalnej synchronizacji zamiast pobierania całego katalogu.Filtrowanie i wyszukiwanie
Wszystkie endpointy produktowe obsługują filtrowanie przez parametry GET i paginację (100 wyników na stronę).
Parametry /api/products-index/:
// Produkty z kategorii ID=5 z rabatem GET /api/products-index/?category=5&discount_prices=true // Nowości - strona 2 GET /api/products-index/?new_product=true&page=2 // Szukaj po fragmencie indeksu GET /api/products-index/?search=ABC // Kategorie i podkategorie GET /api/categories/ GET /api/subcategories/
Struktura odpowiedzi produktu
Pełna struktura JSON jednego produktu z /api/products-index/{index}/.
{ "id": 142, "index": "ABC-123", "quantity": 450, "active": true, "new_product": false, "promotion": false, "sale": false, "discount_prices": true, "category": 5, "subcategory": 12, "names": [ { "id": 1, "language": "name_pl", "title": "Kubek termiczny 350ml" }, { "id": 2, "language": "name_en", "title": "Thermal mug 350ml" } ], "prices": [ { "pln": "24.90", "eur": "5.80", "chf": "5.50", "czk": "138.00" } ], "image": [ { "url": "https://cdn.example.com/products/abc-123-1.jpg" }, { "url": "https://cdn.example.com/products/abc-123-2.jpg" } ], "future_delivery": [ { "quantity": 200, "date": "2026-03-15" } ], "additional": [ { "item": "wymiary", "value": "8.5 x 8.5 x 12 cm" }, { "item": "waga", "value": "210g" } ], "marking_data": [ /* patrz sekcja Znakowanie */ ], // ... names, descriptions, madeof, marking (text) }
Znakowanie — architektura danych
Dane znakowania mają hierarchiczną strukturę. Każdy produkt może mieć wiele miejsc znakowania, a każde miejsce — kilka opcji z różnymi cenami.
Jeden produkt → wiele miejsc (np. “Front”, “Tył”) → wiele opcji (np. “Sitodruk”, “Grawerowanie”) → cena zależy od ilości zamówionych sztuk.
Pojęcia kluczowe:
| Obiekt | Opis | Kluczowe pola |
|---|---|---|
MarkingData | Główny węzeł znakowania produktu. Zawiera domyślną metodę. | product, default_marking_code |
MarkingPlace | Miejsce na produkcie gdzie można nanieść oznaczenie. | code, name_pl |
MarkingOption | Konkretna technika znakowania w danym miejscu. | marking_code, option_code, price_code, max_colors |
MarkingVariant | Wariant opcji (np. rozmiar pola znakowania). | variant_code, variant_label |
MarkingPriceCode | Klucz cenowy — łączy opcję z progami cenowymi. | code |
PriceCode | Próg cenowy dla danej ilości zamówionych sztuk. | from_qty, to_qty, price_pln |
AdditionalService | Usługi dodatkowe do znakowania (np. setup, przesyłka wzoru). | service_id, service_price_pln |
Endpointy znakowania
Dwa tryby dostępu: zagnieżdżony (nested — pełna hierarchia w jednym żądaniu) lub płaski (solo — każdy obiekt osobno, lżejsze odpowiedzi).
Tryb zagnieżdżony (nested):
product__index)Tryb płaski (solo — do szczegółowego zapytania):
?search=<index>?marking_data=ID?marking_place=ID?marking_data=ID// Krok 1 — znajdź ID produktu GET /api/products-index/ABC-123/ // → w odpowiedzi znajdziesz "id": 142 oraz zagnieżdżone "marking_data" // Krok 2 — lub bezpośrednio przez marking-data GET /api/marking-data/?product=142 // Odpowiedź (nested): { "id": 18, "product": 142, "default_marking_code": "TS-F1", "marking_place": [ { "id": 55, "code": "F1", "name_pl": "Przód", "marking_option": [ { "id": 201, "marking_code": "TS-F1", "option_code": "TS", // kod techniki "price_code": 7, // ID → /api/marking-price/ "max_colors": 4, "realisation_time_id": 3, "marking_variant": [] } ] } ], "additional_service": [ { "service_id": "SETUP", "service_price_pln": "35.00" } ] }
Progi cenowe znakowania
Cena za sztukę znakowania zależy od zamówionej ilości. Progi pobierasz przez price_code z opcji znakowania.
GET /api/marking-price/?code=P3 // Odpowiedź: { "id": 7, "code": "P3", "main_marking_price": [ { "from_qty": 25, "to_qty": 49, "price_pln": "2.80" }, { "from_qty": 50, "to_qty": 99, "price_pln": "2.20" }, { "from_qty": 100, "to_qty": 249, "price_pln": "1.60" }, { "from_qty": 250, "to_qty": 499, "price_pln": "1.10" } ] }
Typowy przepływ integracji znakowania
Jak zbudować konfigurator znakowania krok po kroku.
GET /api/products-index/ABC-123/ — w odpowiedzi masz zagnieżdżone marking_data z miejscami i opcjami.marking_place[] wybierz miejsce (np. “Przód”), z marking_option[] wybierz technikę (np. “Sitodruk”). Zanotuj price_code i marking_code.GET /api/marking-price/?code=P3 (lub /api/marking-tierprice/?marking_option=ID) — wyświetl progi cenowe dla ilości klienta.GET /api/marking-additional/?marking_data=ID — dodaj koszty setup, przesyłki wzoru itp.POST /api/orders/ z danymi produktu, ilością i adresem dostawy (znakowanie w przyszłości)./api/orders/ obejmują czysty towar (bez znakowania). Obsługa zamówień ze znakowaniem zostanie dodana w kolejnej wersji API.Profil i uprawnienia
Przed złożeniem pierwszego zamówienia sprawdź swój profil. Administrator musi ustawić trzy kluczowe pola — bez nich zamówienie nie trafi do Vendo lub zostanie zablokowane.
{ "username": "firma_abc", "email": "biuro@firma.pl", "vendo_id": 18479, // null = brak → ZO nie trafi do Vendo "can_order": true, // false = POST /api/orders/ zwróci 403 "discount_percent": 10.00, // % rabatu na produkty z discount_prices=true "currency": "PLN", // waluta używana w zamówieniach "payment_method": "banktransfer", // forma płatności w zamówieniach "language": "pl" }
| Pole | Opis | Kto ustawia |
|---|---|---|
vendo_id | ID klienta w Vendo. Gdy null — zamówienie zapisane, ale nie wysłane do Vendo. | Administrator |
can_order | Gdy false — API odrzuca POST /api/orders/ z błędem 403. | Administrator |
discount_percent | Rabat procentowy naliczany na produkty z discount_prices=true. Ceny CenaNetto0 i CenaNetto są automatycznie wyliczane i przekazywane do Vendo. 0 = brak rabatu. | Administrator |
currency | Waluta używana we wszystkich zamówieniach tego konta. Wpływa na wybór kolumny cenowej (pln / eur / chf / czk). | Administrator |
payment_method | Forma płatności dla zamówień. Domyślnie banktransfer. | Administrator |
Złóż zamówienie
Wystarczy podać listę produktów. Waluta, forma płatności i dane do Vendo pobierane są automatycznie z profilu zalogowanego użytkownika. Zamówienie jest od razu przesyłane do Vendo ERP.
GET /api/me/. Muszą być ustawione:• vendo_id — ID klienta w Vendo (ustawia administrator)
• can_order =
true — uprawnienie do składania zamówień• currency i payment_method — pobierane z profilu, nie trzeba podawać w zamówieniu
• discount_percent — rabat na produkty z
discount_prices=true; ceny finalne liczone i wysyłane do Vendo automatycznie
POST /api/orders/ Authorization: Bearer <access_token> Content-Type: application/json { // Własny numer zamówienia (opcjonalny) → NumerZamówienia w Vendo "client_reference": "MOJ-NR-2024/001", "notes": "Proszę o fakturę VAT", // opcjonalne // Lista produktów — minimum jedna pozycja "items": [ { "product_index": "ABC-123", "quantity": 50 }, { "product_index": "XYZ-007", "quantity": 10 } ] } // Waluta i forma płatności z profilu użytkownika — nie trzeba podawać.
{ "id": 42, "client_reference": "MOJ-NR-2024/001", // ... pola zamawiającego i adresów ... "items": [ { "product_index": "ABC-123", "quantity": 50, "marking_code": "", "marking_place": "", "marking_info": "" }, { "product_index": "XYZ-007", "quantity": 10, "marking_code": "", "marking_place": "", "marking_info": "" } ], "attachments": [], "created_at": "2026-06-25T10:35:00.123Z", // Wynik synchronizacji z Vendo (wypełniany automatycznie) "status": "processing", // new | processing | confirmed | rejected "vendo_client_id": 18479, "vendo_zo_id": 2310073, "sent_to_erp": true, "erp_order_number": "A/2026/0000042", // numer ZO w Vendo "erp_error": "" // puste = sukces }
{ "items": [ { "product_index": ["Produkt o indeksie 'XYZ' nie istnieje lub jest nieaktywny."] } ], "billing_city": ["To pole jest wymagane."] }
{ "detail": "Authentication credentials were not provided." }
Pola zamówienia
Pola żądania (wejściowe):
| Pole | Typ | Opis | |
|---|---|---|---|
items[] | array | Lista pozycji zamówienia. Minimum jedna. | wymagane |
items[].product_index | string | Indeks produktu. Musi istnieć i być aktywny. | wymagane |
items[].quantity | integer | Ilość sztuk ≥ 1. | wymagane |
items[].marking_place_code | string | Kod miejsca znakowania, np. M02. Musi istnieć dla produktu. | opcjonalne |
items[].marking_option_code | string | Kod techniki znakowania, np. C1, S1. | opcjonalne |
items[].marking_colors | integer | Liczba kolorów. Wymagana gdy max_colors > 0. | warunkowe |
items[].marking_info | string | Opis znakowania / instrukcje. | opcjonalne |
client_reference | string | Własny numer zamówienia → NumerZamówienia w Vendo. NumerObcy = API-{id}. | opcjonalne |
notes | string | Uwagi do zamówienia. | opcjonalne |
Pola pobierane automatycznie z profilu użytkownika:
| Pole | Opis |
|---|---|
currency | Waluta zamówienia: PLN / EUR / CHF / CZK. Ustawiana przez admina w profilu. |
payment_method | Forma płatności: banktransfer / prepayment / cod. Domyślnie banktransfer. |
vendo_client_id | ID klienta w Vendo. Bez tego ZO nie zostanie utworzone. |
CenaNetto0 / CenaNetto | Dla produktów z discount_prices=true: cena bazowa i cena po rabacie (discount_percent z profilu) liczone automatycznie i przekazywane do Vendo. Dla pozostałych produktów Vendo używa własnych cen. |
Pola odpowiedzi (tylko do odczytu):
| Pole | Opis |
|---|---|
status | new — nie wysłano | processing — przekazano do Vendo | confirmed — potwierdzone | rejected — odrzucone |
erp_order_number | Pełny numer ZO w Vendo, np. A/2026/0000042. |
vendo_zo_id | Numeryczny ID dokumentu ZO w Vendo. |
erp_error | Błąd Vendo. Pusty = sukces. |
attachments[] | Ogólne załączniki zamówienia. |
GET /api/stock-info/. Profil i uprawnienia: GET /api/me/.Zamówienie ze znakowaniem
Każda pozycja zamówienia może zawierać znakowanie. Podajesz kod miejsca i techniki z danych produktu z API. System weryfikuje, czy takie znakowanie istnieje dla produktu, zanim przyjmie zamówienie.
GET /api/marking-data/?product={id} lub z zagnieżdżonego marking_data[] w odpowiedzi produktu. Szukaj pól code (miejsce) i option_code (technika).Pola pozycji ze znakowaniem:
| Pole | Typ | Opis | |
|---|---|---|---|
marking_place_code | string | Kod miejsca znakowania, np. M02. Musi istnieć dla produktu. | opcjonalne |
marking_option_code | string | Kod techniki znakowania, np. C1, S1, F1. Musi istnieć w danym miejscu. | opcjonalne |
marking_colors | integer | Liczba kolorów. Wymagana gdy technika ma max_colors > 0. | warunkowe |
marking_info | string | Opis znakowania / instrukcje dla grafika. | opcjonalne |
design_files[] | array | Lista plików projektu dla tej pozycji (tylko odczyt). Pliki przesyłasz osobnym żądaniem. | read-only |
POST /api/orders/ Authorization: Bearer <token> Content-Type: application/json { // ... pola zamawiającego i adresy ... "items": [ { "product_index": "20820-14", "quantity": 100, "marking_place_code": "M02", // Tył "marking_option_code":"C1", // DTF 60x50mm "marking_info": "Logo firmowe, projekt w załączniku" }, { "product_index": "20820-14", "quantity": 50, "marking_place_code": "M02", "marking_option_code":"S1", // Sitodruk 300x250mm "marking_colors": 2, // max_colors=3 "marking_info": "Logo 2-kolorowe PMS" } ] }
Upload pliku projektu dla pozycji:
# Krok 1 — złóż zamówienie, zanotuj id zamówienia i id pozycji # W odpowiedzi items[0].id = 99 (id pozycji) # Krok 2 — wyślij plik projektu curl -X POST https://developers.bluecollection.eu/api/orders/42/items/99/design \ -H "Authorization: Bearer $TOKEN" \ -F "file=@/sciezka/do/logo.pdf" // Odpowiedź 201: { "id": 7, "file": "/media/order_item_designs/42/logo.pdf", "uploaded_at": "2026-06-25T11:30:00.000Z" }
SHARED_DESIGNS_PATH/{numer_ZO}/ — w ten sam sposób jak robi to magento_api_sync.MARKING_WEBHOOK_URL z danymi zamówienia i listą pozycji ze znakowaniem.Załączniki
Do każdego zamówienia możesz dołączyć pliki (PDF, PNG, XLSX itp.). Załączniki są zapisywane na serwerze i przesyłane do dokumentu ZO w Vendo jako base64 przy wysyłce.
POST /api/orders/42/attachments/ Authorization: Bearer <token> Content-Type: multipart/form-data // Pole formularza: file // cURL: curl -X POST https://developers.bluecollection.eu/api/orders/42/attachments/ \ -H "Authorization: Bearer $TOKEN" \ -F "file=@/path/to/wzor.pdf" // Odpowiedź 201: { "id": 1, "file": "/media/order_attachments/2026/06/wzor.pdf", "uploaded_at": "2026-06-25T11:00:00.000Z" }
POST /api/orders/{id}/send_to_vendo/ (admin), aby ponownie zsynchronizować i dołączyć plik do ZO.Status & synchronizacja z Vendo
Każde zamówienie jest automatycznie wysyłane do Vendo ERP zaraz po jego utworzeniu. Pole status odzwierciedla wynik synchronizacji.
Zamówienie zapisane, ale nie wysłane do Vendo (brak vendo_client_id lub błąd sieci).
ZO zostało utworzone w Vendo. Pole erp_order_number zawiera jego numer.
Zamówienie potwierdzone po stronie Vendo.
Zamówienie odrzucone po stronie Vendo.
Numeracja w Vendo:
| Pole Vendo | Wartość | Opis |
|---|---|---|
| NumerObcy | API-{id} | Numer nadawany przez ten system. Zawsze w tym formacie. |
| NumerZamówienia | client_reference | Własny numer klienta, jeśli podany w żądaniu. |
Ręczna wysyłka / ponowna próba admin:
POST /api/orders/42/send_to_vendo/ Authorization: Bearer <admin_token> // Odpowiedź 200 — zaktualizowany obiekt zamówienia: { "id": 42, "status": "processing", "erp_order_number":"A/2026/0000042", "erp_error": "" }
erp_error będzie zawierać komunikat „Brak ID klienta w Vendo", a status pozostanie new.Lista zamówień
Pobierz i przeszukuj złożone zamówienia. Filtr status pozwala monitorować kolejkę do Vendo.
GET /api/orders/?status=new Authorization: Bearer <token>
Kody odpowiedzi
Żądanie GET zakończone sukcesem.
Zasób (zamówienie) utworzony pomyślnie.
Błąd walidacji — sprawdź treść odpowiedzi.
Brak lub nieważny token JWT.
Brak uprawnień do zasobu.
Zasób nie istnieje (np. błędny indeks).
Przykłady kodu
# 1. Pobierz token TOKEN=$(curl -s -X POST https://developers.bluecollection.eu/api/token/ \ -H "Content-Type: application/json" \ -d '{"username":"login","password":"haslo"}' \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access'])") # 2. Lista produktów (paginowana) curl https://developers.bluecollection.eu/api/products/ \ -H "Authorization: Bearer $TOKEN" # 3. Szczegóły produktu po indeksie curl https://developers.bluecollection.eu/api/products-index/ABC-123/ \ -H "Authorization: Bearer $TOKEN" # 4. Stany magazynowe curl https://developers.bluecollection.eu/api/stock-info/ \ -H "Authorization: Bearer $TOKEN"
import requests BASE = "https://developers.bluecollection.eu" # 1. Token token = requests.post(f"{BASE}/api/token/", json={ "username": "login", "password": "haslo" }).json()["access"] headers = {"Authorization": f"Bearer {token}"} # 2. Lista produktów (pierwsza strona) products = requests.get(f"{BASE}/api/products/", headers=headers).json() print(f"Łącznie produktów: {products['count']}") # 3. Szczegóły produktu po indeksie product = requests.get(f"{BASE}/api/products-index/ABC-123/", headers=headers).json() print(f"Stan: {product['quantity']} szt.") # 4. Znakowanie produktu marking = requests.get(f"{BASE}/api/marking-data/?product={product['id']}", headers=headers).json() print(marking)
const BASE = "https://developers.bluecollection.eu"; const post = (url, body, tok) => fetch(BASE + url, { method: "POST", headers: { "Content-Type": "application/json", ...(tok && { "Authorization": `Bearer ${tok}` }) }, body: JSON.stringify(body) }).then(r => r.json()); // 1. Token const { access } = await post("/api/token/", { username: "login", password: "haslo" }); // 2. Lista produktów const get = (url) => fetch(BASE + url, { headers: { "Authorization": `Bearer ${access}` } }).then(r => r.json()); const products = await get("/api/products/"); console.log(`Łącznie produktów: ${products.count}`); // 3. Szczegóły produktu po indeksie const product = await get("/api/products-index/ABC-123/"); console.log(`Stan: ${product.quantity} szt.`); // 4. Znakowanie produktu const marking = await get(`/api/marking-data/?product=${product.id}`); console.log(marking);
<?php $base = "https://developers.bluecollection.eu"; function apiPost($url, $data, $token = null) { $ch = curl_init($url); $headers = ["Content-Type: application/json"]; if ($token) $headers[] = "Authorization: Bearer $token"; curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode($data)]); return json_decode(curl_exec($ch)); } // 1. Token $auth = apiPost("$base/api/token/", ["username"=>"login","password"=>"haslo"]); $token = $auth->access; // 2. Lista produktów $ch = curl_init("$base/api/products/"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]); $products = json_decode(curl_exec($ch)); echo "Łącznie produktów: {$products->count}\n"; // 3. Szczegóły produktu po indeksie $ch = curl_init("$base/api/products-index/ABC-123/"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]); $product = json_decode(curl_exec($ch)); echo "Stan: {$product->quantity} szt.\n"; print_r($product);
developers.bluecollection.eu. Dane logowania uzyskasz od administratora.