API dla partnerów
Dostęp do katalogu, zamówień i dokumentów konta B2B. Ceny są takie same jak na stronie po zalogowaniu.
Adres: https://b2b.z-ecoenergy.com/api/v1. Zapytanie przez HTTP zwraca 400 https_required bez przekierowania.
Logowanie
POST /api/v1/login
Content-Type: application/json
{"email": "biuro@twojafirma.pl", "password": "…", "accept_price_confidentiality": true}{
"token": "zk1.…",
"expires_at": "2026-09-27T15:20:00.000Z",
"feed": {"csv": "https://b2b.z-ecoenergy.com/api/v1/feed/zf1.….csv", "xml": "…", "ceneo": "…", "skyshop": "…"}
}import requests
API = "https://b2b.z-ecoenergy.com/api/v1"
r = requests.post(f"{API}/login", json={
"email": "biuro@twojafirma.pl",
"password": "…",
"accept_price_confidentiality": True,
})
headers = {"Authorization": "Bearer " + r.json()["token"]}accept_price_confidentiality: true oznacza zgodę na poufność cen, tę samą co przy logowaniu na stronie.
Token jest ważny 2 godziny i jest przesyłany w nagłówku każdego zapytania:
Authorization: Bearer zk1.…
Wygasły token zwraca 401 unauthorized.
Produkty
Produkt jest identyfikowany linkiem na stronie (url).
GET /api/v1/products
GET /api/v1/products?url=/product/produkt-1&url=/product/produkt-2
GET /api/v1/products?currency=EURr = requests.get(f"{API}/products", headers=headers, params={"currency": "EUR"})
products = r.json()["products"]Bez parametrów zwracany jest cały katalog. url przyjmuje pełny link albo ścieżkę. currency: PLN (domyślnie), EUR, USD.
{
"currency": "PLN",
"products": [
{"url": "https://b2b.z-ecoenergy.com/product/produkt-1", "name": "Przykładowy produkt", "code": "PRODUKT-1",
"stock": 12, "price_net": 100.00, "price_gross": 123.00},
{"url": "https://b2b.z-ecoenergy.com/product/produkt-2", "name": "Przykładowy moduł", "code": "PRODUKT-2",
"stock": 900, "price_net": 300.00, "price_gross": 369.00, "pack": 36,
"tiers": [{"min_quantity": 180, "price_net": 290.00, "price_gross": 356.70}]}
]
}
code: kod produktu.price_net,price_gross: cena konta za sztukę.nulloznacza cenę na zapytanie. W EUR i USDprice_grossjestnull.pack: wielkość opakowania. Pole występuje tylko przy produktach sprzedawanych w opakowaniach, zamawiana ilość jest jego wielokrotnością.tiers: ceny od danej ilości. Pole występuje tylko przy produktach z progami ilościowymi.
Odpowiedź zawiera nagłówek ETag. Zapytanie z tą wartością w If-None-Match zwraca pustą odpowiedź 304, jeśli dane się nie zmieniły.
Zamówienie
POST /api/v1/orders
Authorization: Bearer zk1.…
Content-Type: application/json
{
"items": [
{"url": "https://b2b.z-ecoenergy.com/product/produkt-1", "quantity": 2},
{"code": "PRODUKT-2", "quantity": 36}
],
"currency": "PLN",
"payment": "transfer",
"delivery": {
"company": "Firma Klienta", "first_name": "Jan", "last_name": "Kowalski",
"street": "Polna 1", "postcode": "95-030", "city": "Rzgów", "country": "PL",
"phone": "600100200"
},
"reference": "10234",
"notes": "Proszę o kontakt przed dostawą",
"accept_terms": true
}201 {"status": "accepted", "reference": "10234"}r = requests.post(f"{API}/orders", headers=headers, json={
"items": [{"url": "/product/produkt-1", "quantity": 2}],
"currency": "PLN",
"payment": "transfer",
"pickup": True,
"reference": "10234",
"accept_terms": True,
})| Pole | Opis |
|---|---|
items | url produktu albo jego code (kod produktu, ten sam co w plikach z ofertą) oraz quantity (liczba całkowita, przy produktach z pack wielokrotność pack) |
currency | PLN, EUR lub USD. Firma z siedzibą w Polsce zawsze dostaje proformę w PLN z VAT, niezależnie od tego pola. Firma zagraniczna dostaje proformę w podanej walucie, bez VAT; PLN zamieniamy wtedy na EUR. |
payment | transfer (przelew), cash_on_delivery (za pobraniem, tylko z dostawą) lub cash (gotówka, tylko odbiór osobisty) |
pickup | true: odbiór osobisty w Rzgowie, bez delivery |
delivery | Adres dostawy. Wymagane first_name, postcode, city. country: kod kraju ISO, np. DE. |
reference | Numer zamówienia po stronie klienta: litery, cyfry, . _ / -, do 40 znaków |
notes | Uwagi, do 300 znaków |
accept_terms | true: akceptacja regulaminu |
Zamówienie jest zapisywane w ERP jako proforma. Stany są sprawdzane przy każdym zamówieniu. Brak na stanie lub ilość niebędąca wielokrotnością pack zwraca błąd.
Odstęp między zamówieniami z jednego konta wynosi co najmniej 2 minuty. Wcześniejsze zamówienie zwraca 429 order_too_soon.
Odpowiedź nie zawiera numeru proformy. Proformę zwraca GET /api/v1/proformas?reference=10234, również po zerwanym połączeniu, gdy odpowiedź na zamówienie nie dotarła.
Proformy
GET /api/v1/proformas
GET /api/v1/proformas?reference=10234{"proformas": [{
"number": "OF/FPF/26/000001", "date": "2026-09-27", "currency": "PLN",
"net": 200.00, "gross": 246.00, "status": "Nie weryfikowana", "reference": "10234",
"pdf_url": "https://b2b.z-ecoenergy.com/api/v1/proformas/OF-FPF-26-000001/pdf"
}]}r = requests.get(f"{API}/proformas", headers=headers, params={"reference": "10234"})
pdf = requests.get(r.json()["proformas"][0]["pdf_url"], headers=headers)pdf_url wymaga tego samego nagłówka Authorization. null oznacza, że PDF proformy nie jest dostępny przez API. Nowa proforma ma status "Nie weryfikowana" i pdf_url równe null, dopóki biuro nie doliczy transportu i jej nie zatwierdzi.
Faktury i wysyłki
GET /api/v1/invoices
GET /api/v1/shipmentsinvoices = requests.get(f"{API}/invoices", headers=headers).json()["invoices"]
shipments = requests.get(f"{API}/shipments", headers=headers).json()["shipments"]Pola faktury: number, date, currency, net, gross, pdf_url. Wysyłka:
{"shipments": [{
"number": "WZ/RZ /26/000001", "date": "2026-09-28", "status": "Wysłane",
"carrier": "DPD", "tracking_number": "0000000000000",
"pdf_url": "https://b2b.z-ecoenergy.com/api/v1/shipments/WZ-RZ-26-000001/pdf"
}]}
carrier i tracking_number są null do wydania paczki z magazynu.
Numery seryjne
GET /api/v1/serial-numbers{"documents": [{
"number": "WZ/1 /26/000001", "date": "2026-09-20",
"url": "https://b2b.z-ecoenergy.com/api/v1/serial-numbers/WZ-1-26-000001",
"pdf_url": "https://b2b.z-ecoenergy.com/api/v1/serial-numbers/WZ-1-26-000001/pdf"
}]}url zwraca numery seryjne z dokumentu:
{"number": "WZ/1 /26/000001", "items": [{"name": "Przykładowy produkt", "serial": "SN0000001"}]}docs = requests.get(f"{API}/serial-numbers", headers=headers).json()["documents"]
items = requests.get(docs[0]["url"], headers=headers).json()["items"]Błędy
Błąd ma postać {"error": "kod"}, niektóre z dodatkowymi polami.
| HTTP | error | Znaczenie |
|---|---|---|
| 400 | https_required | Zapytanie przez HTTP |
| 400 | credentials_required, confidentiality_required, invalid_json, currency_invalid | Niepoprawne zapytanie |
| 400 | items_required, duplicate_item, quantity_invalid (z urls), quantity_not_pack_multiple (z items), terms_required, payment_invalid, payment_not_allowed, delivery_required, postcode_invalid, phone_invalid, country_invalid, reference_invalid | Niepoprawne zamówienie |
| 401 | wrong_credentials, unauthorized | Błędne dane logowania lub wygasły token |
| 404 | not_found, pdf_not_available | Brak zasobu na tym koncie |
| 405 | method_not_allowed | Nieobsługiwana metoda HTTP |
| 409 | pdf_not_ready | Proforma czeka na weryfikację przez biuro, PDF będzie dostępny po niej |
| 409 | unknown_items (z urls: wysłane url lub code), unavailable_items, stock_exceeded (z items: url, requested, available) | Pozycje nieznane, niedostępne lub ponad stan |
| 413, 415 | body_too_large, json_required | Treść ponad 1 MB lub typ inny niż application/json |
| 422 | order_rejected | Zamówienie odrzucone przez ERP |
| 429 | order_too_soon, rate_limited | Limit zapytań, czas w nagłówku Retry-After |
| 502, 503 | order_failed, unavailable, pdf_unavailable | Chwilowa niedostępność ERP |
Integracje
Pliki z ofertą konta do importu w sklepie internetowym. Link zawiera klucz konta i przestaje działać po zmianie hasła. Linki są też w polu feed odpowiedzi na logowanie. Zamiast klucza można użyć HTTP Basic (e-mail i hasło konta): GET /api/v1/feed.xml, feed.csv, feed.ceneo.xml, feed.skyshop.xml, feed.full.xml.
r = requests.get(f"{API}/feed.xml", auth=("biuro@twojafirma.pl", "…")) XML / CSV
currency: PLN (domyślnie), EUR, USD. W EUR i USD price_gross jest puste. separator=semicolon: CSV ze średnikiem jako separatorem. Puste price_net oznacza cenę na zapytanie.
GET /api/v1/feed/{klucz}.xml
GET /api/v1/feed/{klucz}.csv
GET /api/v1/feed/{klucz}.xml?currency=EUR
GET /api/v1/feed/{klucz}.csv?separator=semicolon<?xml version="1.0" encoding="UTF-8"?>
<products currency="PLN">
<product>
<url>https://b2b.z-ecoenergy.com/product/produkt-1</url>
<name>Przykładowy produkt</name>
<stock>12</stock>
<price_net>100</price_net>
<price_gross>123</price_gross>
<code>PRODUKT-1</code>
</product>
</products>url,name,stock,price_net,price_gross,code
https://b2b.z-ecoenergy.com/product/produkt-1,Przykładowy produkt,12,100,123,PRODUKT-1Ceneo
Format Ceneo, ceny netto konta w PLN. avail: 1 na stanie, 90 na zamówienie. Produkty bez ceny są pominięte.
GET /api/v1/feed/{klucz}.ceneo.xml<?xml version="1.0" encoding="UTF-8"?>
<offers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1">
<o id="PRODUKT-1" url="https://b2b.z-ecoenergy.com/product/produkt-1" price="100.00" avail="1" stock="12">
<cat><![CDATA[Moduły fotowoltaiczne]]></cat>
<name><![CDATA[Przykładowy produkt]]></name>
<imgs>
<main url="https://b2b.z-ecoenergy.com/cdn/product_1/images/1.png" />
<i url="https://b2b.z-ecoenergy.com/cdn/product_1/images/2.png" />
</imgs>
<desc><![CDATA[Przykładowy produkt]]></desc>
<attrs>
<a name="Kod Producenta"><![CDATA[PRODUKT-1]]></a>
<a name="Jednostka"><![CDATA[SZT]]></a>
<a name="Producent"><![CDATA[Producent]]></a>
</attrs>
</o>
</offers>Skyshop
Format Skyshop w PLN. prod_price: cena brutto, prod_tax_id: cena netto, prod_weight: waga w gramach. Produkty bez ceny są pominięte.
GET /api/v1/feed/{klucz}.skyshop.xml<?xml version="1.0" encoding="UTF-8"?>
<products xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1" date="2026-09-30 11:00:00">
<item>
<prod_name_pl><![CDATA[Przykładowy produkt]]></prod_name_pl>
<prod_name_en><![CDATA[Example product]]></prod_name_en>
<prod_name_de><![CDATA[Beispielprodukt]]></prod_name_de>
<prod_desc_pl><![CDATA[<p>…</p>]]></prod_desc_pl>
<prod_desc_en><![CDATA[<p>…</p>]]></prod_desc_en>
<prod_desc_de><![CDATA[<p>…</p>]]></prod_desc_de>
<prod_img>
<img>https://b2b.z-ecoenergy.com/cdn/product_1/images/1.png</img>
<img>https://b2b.z-ecoenergy.com/cdn/product_1/technicalData/karta.pdf</img>
</prod_img>
<cat_path>Moduły fotowoltaiczne/Producent</cat_path>
<prod_id>1</prod_id>
<prod_price>123.00</prod_price>
<prod_tax_id>100.00</prod_tax_id>
<taxpercent>23</taxpercent>
<prod_amount>12</prod_amount>
<prod_weight>22000</prod_weight>
<prod_symbol><![CDATA[PRODUKT-1]]></prod_symbol>
<prd_name>Producent</prd_name>
</item>
</products>Pełny XML
Wszystko, co system ma o produkcie, w jednym pliku z polskimi nazwami pól: kategoria, kod, nazwa, zdjęcia, dokumenty (karty katalogowe, certyfikaty), opis HTML, cena netto konta, stan, producent, waga w kg oraz parametry odczytane z katalogu: liczba faz, moc (moduły w Wp, falowniki w W), wymiary i kolor ramy modułów, pojemność magazynów. Pola, których system nie zna dla danego produktu, są pominięte. Produkty bez ceny są pominięte.
kategoria: tylko ta kategoria, podana identyfikatorem albo nazwą z pola <kategoria>, np. gotowe-zestawy lub Gotowe zestawy. Identyfikatory: gotowe-zestawy, panele-sloneczne, magazyny-energii, ladowarki, optymalizatory-mocy, akcesoria-do-inwerterow, systemy-montazowe, pompy-ciepla, falowniki. Bez parametru plik zawiera te dziewięć kategorii, każdy produkt raz, w pierwszej z nich w tej kolejności.
wszystkie=1: dołącza też pozostałe kategorie sklepu, po tych dziewięciu: akcesoria-do-magazynow-energii, akcesoria-fotowoltaiczne, podgrzewacze-cwu, klimatyzatory, rekuperatory. Każda z nich działa też osobno w kategoria. currency: PLN (domyślnie), EUR, USD.
Pierwsze zapytanie po restarcie serwera może zwrócić 503 unavailable z nagłówkiem Retry-After, dopóki plik nie zostanie zbudowany.
GET /api/v1/feed/{klucz}.full.xml
GET /api/v1/feed/{klucz}.full.xml?kategoria=falowniki
GET /api/v1/feed/{klucz}.full.xml?wszystkie=1<?xml version="1.0" encoding="UTF-8"?>
<produkty>
<produkt>
<kategoria>Falowniki</kategoria>
<kod_producenta>PRODUKT-1</kod_producenta>
<nazwa_produktu>Przykładowy falownik hybrydowy 10 kW 3-fazowy</nazwa_produktu>
<zdjecia>
<zdjecie>https://b2b.z-ecoenergy.com/cdn/product_1/images/1.png</zdjecie>
</zdjecia>
<dokumenty_techniczne_pdf_linki>
<dokumenty_techniczne>
<dokument_1>
<etykieta>Karta katalogowa</etykieta>
<url>https://b2b.z-ecoenergy.com/cdn/product_1/technicalData/karta.pdf</url>
</dokument_1>
</dokumenty_techniczne>
</dokumenty_techniczne_pdf_linki>
<opis_produktu><![CDATA[<p>…</p>]]></opis_produktu>
<cena_netto_za_szt>100.00</cena_netto_za_szt>
<dostepna_ilosc_szt_w_magazynie>12</dostepna_ilosc_szt_w_magazynie>
<moc_znam>10000W</moc_znam>
<ilosc_faz>3</ilosc_faz>
<producent>Producent</producent>
<magazyn>Rzgów</magazyn>
<masa_kg>19</masa_kg>
<moc>10000W</moc>
<typ>Hybrydowy</typ>
</produkt>
</produkty>