Sposób działania
Import produktów to proces dwuetapowy (ładowanie wsadowe):
-
POST
/import/products/v2/load_data— wgrywa zbiór definicji produktów (plik JSON) do bufora danych. Można go wywołać wielokrotnie z parametremcontinueUpload=1, aby doklejać kolejne paczki do bufora (continueUpload=0czyści bufor przed wgraniem). -
POST
/import/products/v2/run— przenosi dane z bufora do bazy produkcyjnej (jednorazowo, dla całej zawartości bufora).
Podczas przenoszenia system sprawdza, czy dany kod produktu (pole code) już istnieje. Jeśli produkt o tym kodzie już istnieje — zostaje pominięty (import nie nadpisuje istniejących produktów).
Tworzenie vs aktualizacja — to jest endpoint do DODAWANIA nowych produktów.
Ponieważ istniejące kody są pomijane, nie da się tym endpointem zaktualizować już istniejącego produktu — w szczególności jego flagi stock_enabled, cen ani jednostek sprzedażowych. Ponowny import tego samego code nie wprowadzi żadnych zmian.
Aby zaktualizować istniejący produkt (w tym stock_enabled, ceny, jednostki sprzedażowe), użyj endpointu POST /import/product/update — patrz sekcja Aktualizacja istniejących produktów na końcu artykułu.
Jak wysłać request
Endpoint load_data przyjmuje dane jako multipart/form-data — NIE jako surowe body JSON. Tablica produktów (JSON) jest wysyłana jako plik w polu formularza file. {instancja} = adres API Twojej instancji Saly (np. https://twoja-domena/api).
Krok 1 — token. Wyślij POST na /user/token w formacie application/x-www-form-urlencoded z polami username oraz hasłem. W odpowiedzi otrzymasz access_token — wstaw go do nagłówka autoryzacji (typ Bearer) w kolejnych żądaniach.
Krok 2 — load_data (POST /import/products/v2/load_data), żądanie typu multipart/form-data:
|
Element żądania |
Wartość |
|---|---|
|
Nagłówek autoryzacji |
typ Bearer z tokenem z kroku 1 |
|
Pole formularza |
plik JSON z tablicą definicji produktów, |
|
Pole formularza |
|
Krok 3 — run (POST /import/products/v2/run): nagłówek Bearer, parametry w query string: truncate_all_products oraz truncate_all_categories (zwykle oba 0). Odpowiedź zawiera report (patrz „Odpowiedź i raport").
Gotowe polecenia curl dla wszystkich trzech kroków znajdziesz w zewnętrznej dokumentacji technicznej API. Najważniejsze: payload to plik w polu file (multipart), a nie surowe body JSON.
Typy produktów
Tym endpointem można dodać dwa typy produktów:
-
Produkt pojedynczy
-
Rodzina produktów (warianty)
Produkt pojedynczy — dokumentacja pól
Legenda kolumny typ: * pole wymagane · X dowolne znaki · [0-1] tylko 0 lub 1 · [0-9] liczby · R2 liczba dziesiętna z 2 miejscami · JSON obiekt/tablica JSON · (x) limit znaków · date (YYYY-mm-dd) data.
code wymagane
|
Opis |
Przykład |
Typ |
|---|---|---|
|
Unikalny kod produktu w Saly. Klucz identyfikujący produkt — po nim sprawdzane jest, czy produkt już istnieje. |
|
X (255) * |
additional_code
|
Pole |
Opis |
Przykład |
Typ |
|---|---|---|---|
|
|
Numer EAN (13 cyfr) identyfikujący produkt |
|
[0-9] (13) |
|
|
Stock Keeping Unit — kod identyfikujący produkt |
|
X (45) |
|
|
Kod produktu nadany przez dostawcę |
|
X (255) |
information
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Nazwa produktu w językach (pl, en, de, ru, es, fr, it). Format JSON jak w przykładzie. |
JSON |
|
|
Opis produktu w językach (HTML dozwolony). |
JSON |
|
|
Czy produkt jest publikowany na froncie (0 = nie [domyślnie], 1 = tak) |
[0-1] |
|
|
Czy produkt jest promowany (0 = nie [domyślnie], 1 = tak) |
[0-1] |
metadata
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
SEO: meta tytuł w językach |
JSON |
|
|
SEO: słowa kluczowe w językach |
JSON |
|
|
SEO: meta opis w językach |
JSON |
|
|
Przyjazny URL w językach |
JSON |
categories
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Wszystkie kategorie, w których jest produkt. Pierwsza kategoria jest główną. Pełna ścieżka, separator |
JSON |
price
|
Pole |
Opis |
Przykład |
Typ |
|---|---|---|---|
|
|
Cena katalogowa (sprzedażowa) netto — to jest cena, od której liczone są rabaty i którą widzi klient jako cenę produktu w jednostce podstawowej. |
|
R2 |
|
|
Minimalna cena netto, za którą produkt może być sprzedany (próg). |
|
R2 |
|
|
Cena zakupu netto. |
|
R2 |
|
|
Stawka VAT produktu (cecha produktu). |
|
[0-9] |
Cena jednostki sprzedażowej = cena katalogowa × rate tej jednostki, z opcjonalnym rabatem discount — patrz sekcja units.
manufacturer
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Nazwa producenta |
X (45) |
|
|
URL do logo producenta |
X (255) |
energy_class
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Klasa energetyczna produktu |
[A-G] (1) |
|
|
Link do etykiety klasy energetycznej (PDF) |
X (255) |
|
|
Link do karty informacyjnej produktu (PDF) |
X (255) |
stock — stany magazynowe i flaga "stock impact"
|
Pole |
Opis |
Przykład |
Typ |
|---|---|---|---|
|
|
Flaga „Włącz stany magazynowe dla produktu" (stock impact). |
|
[0-1] |
Ważne o stock_enabled:
-
To jest tylko flaga (czy produkt śledzi stan), NIE ilość. Poziomy stanu (ilości w magazynach) wgrywa się osobnym importem stanów (
/import/product_stocks), nie tym endpointem. -
Aktualizacja
stock_enabledna istniejącym produkcie nie zadziała przez ten endpoint (istniejące kody są pomijane) — użyj/import/product/update. -
Jednostki sprzedażowe definiuje się wyłącznie w bloku
units— polestock.unitnie istnieje i jest ignorowane.
delivery_date — czas dostawy zapisywany do magazynów (tablica wpisów per magazyn):
|
Pole |
Opis |
Przykład |
Typ |
|---|---|---|---|
|
|
Czas dostawy (w językach). Pusty → dziedziczony z magazynu. |
|
JSON |
|
|
Czas dostawy w godzinach |
|
[0-9] |
|
|
External ID magazynu, którego dotyczy wpis |
|
[0-9] |
|
|
Spodziewana data uzupełnienia |
|
date |
units — jednostki sprzedażowe i ich ceny
Sekcja units to tablica jednostek sprzedażowych produktu. Każda jednostka musi odwoływać się (przez external_id) do jednostki wcześniej zdefiniowanej w Słownikach → Jednostki sprzedażowe w panelu administratora.
Model i ceny jednostek
-
Jednostka podstawowa (primary) — baza wszystkich przeliczeń i jednostka, w której śledzony jest stan magazynowy. Jej
ratejest zawsze 1 (niezmienny). -
rate(przelicznik) — ile jednostek podstawowych mieści jedna ta jednostka. Cena tej jednostki = cena katalogowa (srp_netto) ×rate. -
discount(rabat %) — rabat naliczany, gdy wybrana jest ta jednostka. -
minimum_sales_quantity— minimalna ilość zamówienia w tej jednostce (liczba całkowita > 0). -
Jednostka domyślna (default) — wstępnie wybrana przy wyświetlaniu; musi być aktywna.
-
active— czy jednostka jest widoczna i wybieralna przez klienta.
Gwarancja integralności (egzekwowana przez importer): każdy produkt po imporcie ma dokładnie jedną aktywną jednostkę podstawową oraz dokładnie jedną aktywną jednostkę domyślną. Jeśli w bloku units nie oznaczysz żadnej jednostki jako primary_unit=1 (albo jednostka okaże się nieprawidłowa), importer automatycznie wybierze jednostkę podstawową na podstawie domyślnych ustawień Słownika — produkt nigdy nie zostanie bez jednostki podstawowej.
Brak sekcji units — zachowanie słownikowe
Dodanie sekcji units jest opcjonalne. Gdy jej nie podasz, jednostki nowego produktu zostaną ustawione zgodnie z flagami domyślności w Słowniku. Trzy flagi słownika są honorowane niezależnie:
|
Flaga w Słowniku |
Ustawia na produkcie |
|---|---|
|
Domyślnie aktywna |
|
|
Domyślnie prezentowana |
|
|
Domyślna jednostka podstawowa |
|
Niezależność oznacza, że np. jednostka z Domyślnie aktywna = ON i Domyślnie prezentowana = OFF będzie na produkcie aktywna, ale NIE domyślna. Jednostka bez żadnej z flag pozostanie nieaktywna.
Pola jednostki
|
Pole |
Opis |
Przykład |
Typ |
|---|---|---|---|
|
|
Kod jednostki — musi już istnieć w Słowniku. Nieznany kod → jednostka odrzucona (zgłaszana w raporcie, patrz niżej). |
|
X (20) |
|
|
Czy to jednostka podstawowa (przeliczniki i stan liczone względem niej). Maks. jedna na produkt. |
|
[0-1] |
|
|
Czy to jednostka domyślna (wstępnie wybrana). Maks. jedna na produkt; musi być aktywna. |
|
[0-1] |
|
|
Czy jednostka jest aktywna (widoczna/wybieralna). |
|
[0-1] |
|
|
Przelicznik względem jednostki podstawowej (dla primary = 1). |
|
R2 |
|
|
Rabat (%) naliczany dla tej jednostki (≥ 0). |
|
[0-9] |
|
|
Minimalna ilość zamówienia w tej jednostce (liczba całkowita > 0). |
|
[0-9] |
package
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Nazwa opakowania zdefiniowanego wcześniej w systemie |
X (255) |
|
|
Wymiary opakowania w centymetrach |
R2 |
|
|
Waga opakowania w kilogramach |
R2 |
attributes
Parametry techniczne produktu pogrupowane i w docelowej kolejności wyświetlania, w językach (JSON). Format jak w przykładzie poniżej.
multimedia
|
Pole |
Opis |
Typ |
|---|---|---|
|
|
Lista URL-i zdjęć produktu (bez limitu) |
JSON |
|
|
Lista par |
JSON |
Odpowiedź i raport importu
Import jest nieblokujący — poprawne produkty zostaną zaimportowane, a o problemach z poszczególnymi jednostkami dowiesz się z odpowiedzi (batch nie jest odrzucany).
-
/load_datazwraca m.in. liczniki wgranych wierszy orazstock_unit_ignored— listę kodów produktów, które przysłały nieobsługiwane polestock.unit(przypomnienie: jednostki definiuj w blokuunits). -
/runzwraca{"status":"ok","report":{...}}, gdziereportzawiera:-
invalid_units— lista{product_code, external_id, reason}jednostek odrzuconych (np. nieznanyexternal_id, nie-całkowite/ujemneminimum_sales_quantity, ujemnyrate/discount, wiele jednostek domyślnych); -
products_without_primary_fallback_applied— produkty, dla których importer musiał automatycznie ustalić jednostkę podstawową (gdy payload jej nie wskazał); -
proc_errors— błędy procedury (jeśli wystąpiły).
-
Dzięki temu masz pewność, które jednostki zostały przyjęte, a które wymagają poprawy w kolejnym imporcie.
Aktualizacja istniejących produktów
Endpoint /import/products/v2/* tylko dodaje nowe produkty. Aby zmienić dane istniejącego produktu — w tym stock_enabled, ceny i jednostki sprzedażowe — użyj:
POST /import/product/update
-
Aktualizuje wskazane pola istniejących produktów (m.in.
stock_enabled, ceny, jednostki). -
Dla jednostek sterowanie trybem przez
units_increase:0= zastąp komplet jednostek nowym zestawem;1= aktualizacja częściowa (modyfikuje/dodaje przesłane jednostki, resetując konkurujące primary/default). Pusta tablicaunits→ istniejące jednostki pozostają bez zmian. -
Reguły integralności jednostek (dokładnie 1 primary, 1 aktywna default) obowiązują tak samo.
Przykładowy plik
[
{
"code": "5907595646406",
"additional_code": {
"ean": "5907595646406",
"sku": "Mac Book Air 13,3",
"supplier_product_code": "5907595646406"
},
"information": {
"name": { "pl": "MacBook Air 13 gwiezdna szarość", "en": "MacBook Air 13 Space Gray" },
"description": { "pl": "<h4>MacBook Air 13</h4><p>opis</p>" },
"is_active": "1",
"is_promoted": "0"
},
"metadata": {
"metatitle": { "pl": "MacBook Air 13 gwiezdna szarość" },
"keywords": { "pl": "MacBook,Air,13" },
"metadescription": { "pl": "MacBook Air 13 - najlepsza cena" },
"slug": { "pl": "MacBook_Air_13_gwiezdna_szarosc" }
},
"categories": {
"paths": [ { "pl": "Laptopy/Apple/MacBook Air 13" } ]
},
"price": {
"base_netto": "4000.00",
"purchase_netto": "3300.99",
"srp_netto": "4499.99",
"vat": "23"
},
"manufacturer": { "name": "Apple", "logo": "https://www.apple.com/logo.png" },
"energy_class": {
"energy_class": "A",
"energy_label_link": "https://link.pdf",
"energy_card_information_link": "https://link.pdf"
},
"stock": {
"stock_enabled": "1",
"delivery_date": [
{ "delivery_format": {"pl": "7 dni"}, "delivery_hour": "168", "warehouse_external_id": "1", "restocking_date": "2025-02-02" }
]
},
"package": {
"name": "Euro-pallet",
"dimensions": { "height": "100.00", "width": "100.00", "length": "100.00", "weight": "10.00" }
},
"attributes": {
"pl": [ { "Dane techniczne": [ ["Kolor", "czerwony"], ["Rozmiar", "średni"] ] } ]
},
"multimedia": {
"photos": [ "https://example.com/photo1.jpg" ],
"files": [ ["file1.pdf", "https://example.com/file1.pdf"] ]
},
"units": [
{ "external_id": "szt.", "primary_unit": "1", "default_unit": "1", "active": "1", "rate": "1.0", "discount": "0", "minimum_sales_quantity": "1" },
{ "external_id": "opk", "primary_unit": "0", "default_unit": "0", "active": "1", "rate": "10.0", "discount": "10", "minimum_sales_quantity": "1" }
]
}
]