BiznisWeb Partner API
Partner API umožňuje integračným partnerom pristupovať k privilegovaným funkciám BiznisWeb GraphQL API — pokročilé filtre, bulk operácie, mutácie obsahu, content pages, custom order info, tracking URLs a ďalšie operácie, ktoré nie sú dostupné cez bežný webový token.
Tento dokument je v2 a obsahuje skúsenosti reálnych integrácií vrátane gotcha, ktoré nie sú v pôvodnej dokumentácii zrejmé. Pre rýchlu referenciu pôvodnej špecifikácie pozri stránku Volanie API.
1. Predpoklady
Pred prvým volaním Partner API potrebujete:
| Údaj | Kde získať | Príklad |
|---|---|---|
| Partner ID | Od BiznisWebu po podpise partnerskej zmluvy | Demo-Integrator-2026 |
| Partner secret | Od BiznisWebu spolu s Partner ID | aB3$dEf... (string, ~12–32 znakov) |
| Webový API token | Klient (majiteľ eshopu) vygeneruje v admin paneli › Nastavenia › BiznisWeb API; pošle Vám ho | 32 znakov, napr. Ab3xY9pQrSt2uVw5xYz8aB1cDeF4gH6i |
| Client domain | BiznisWeb interný identifikátor eshopu — VŽDY v tvare <subdoména>.flox.sk alebo <subdoména>.flox.cz |
eshop1.flox.sk |
| API endpoint domain | Verejná doména, kde API skutočne počúva. Môže byť rovnaká ako client domain, alebo vlastná doména eshopu | www.eshop1.sk |
API endpoint domain a Client domain sa nemusia zhodovať. Sú to dve rôzne hodnoty s rôznymi účelmi:
- API endpoint domain — kam posielate HTTP request
- Client domain — vstup do SHA1 hashu (BiznisWeb interná identita eshopu)
*.flox.sk/*.flox.cz (žiadna vlastná doména), sú obe rovnaké. Pre eshopy s vlastnou doménou (najmä SK eshopy s .sk alebo .eu) sa líšia. Pozri sekciu 3.2. Autentizácia — dva tokeny
Každý partnerský request musí obsahovať oba headery:
BW-API-Key: Token <32-znakový webový token>
BW-Partner-Key: <partner-id> <40-znakový SHA1 hash>
Content-Type: application/json
2.1 Webový token REQUIRED
32-znakový string vygenerovaný klientom v admin paneli eshopu. Header value musí obsahovať prefix Token (so medzerou) pred tokenom:
BW-API-Key: Token Ab3xY9pQrSt2uVw5xYz8aB1cDeF4gH6i
Tieto formáty nefungujú a vrátia HTTP 401:
X-Api-Key: Ab3xY...
Authorization: Bearer Ab3xY...
BW-API-Key: Ab3xY... ← chýba "Token " prefix
2.2 Partner token REQUIRED
Header value je partner ID, medzera, a 40-znakový SHA1 hash:
BW-Partner-Key: Demo-Integrator-2026 d2f7e3a8b4c1e9f0a5b2c7d8e1f4a9b6c3d5e7a8
BW-Partner-Key: Partner d2f7e3... ← prefix musí byť partner ID, nie slovo "Partner"
BW-Partner-Key: d2f7e3... ← chýba partner ID prefix
BW-Partner-Key: Demo-Integrator-2026 raw_secret ← musí to byť HASH, nie sám secret
2.3 Výpočet partner hashu
Hash je SHA1 (nie MD5, nie SHA256) z konkatenácie troch hodnôt v presnom poradí:
partner_hash = sha1(client_domain + partner_secret + partner_id)
Výstup je presne 40 hex znakov. Ak vidíte 32 znakov, použili ste MD5 — to je chyba.
| Vstup | Popis | Príklad |
|---|---|---|
client_domain |
VŽDY *.flox.sk alebo *.flox.cz subdomain, nie verejná doména eshopu |
eshop1.flox.sk |
partner_secret |
Tajný kľúč od BiznisWebu — používajte raw, bez prefixov | prikladSecret*** |
partner_id |
Váš partner identifier | Demo-Integrator-2026 |
Vývojári často omylom použijú verejnú doménu eshopu (
www.eshop1.sk) namiesto client domain (eshop1.flox.sk). Hash potom nevalidne — server vracia HTTP 412.Pravidlo: client domain je vždy
*.flox.sk/*.flox.cz subdomain, aj keď eshop má vlastnú doménu.Konkrétny príklad (Python)
import hashlib
client_domain = "eshop1.flox.sk"
partner_secret = "<your-partner-secret>"
partner_id = "Demo-Integrator-2026"
partner_hash = hashlib.sha1(
(client_domain + partner_secret + partner_id).encode("utf-8")
).hexdigest()
print(partner_hash)
# → d2f7e3a8b4c1e9f0a5b2c7d8e1f4a9b6c3d5e7a8 (40 hex chars, ilustračná hodnota)
header_value = f"{partner_id} {partner_hash}"
# → "Demo-Integrator-2026 d2f7e3a8b4c1e9f0a5b2c7d8e1f4a9b6c3d5e7a8"
3. Endpoint URL — API endpoint domain vs Client domain
Cieľová URL pre HTTP POST je vždy:
https://<api-endpoint-domain>/api/graphql
Pri partner volaní existujú dve oddelené hodnoty:
| Pole | K čomu | Príklad pre eshop s vlastnou doménou | Príklad pre eshop bez vlastnej domény |
|---|---|---|---|
| API endpoint domain | URL host kde API skutočne počúva | www.eshop1.sk |
eshop2.flox.cz |
| Client domain | Vstup do SHA1 hashu (interná BW identita) | eshop1.flox.sk |
eshop2.flox.cz |
Vždy posielajte request na verejnú doménu eshopu (ak existuje), nie na
*.flox.sk. Flox subdomain robí HTTP 308 redirect na primary domain a klient, ktorý redirect bezhlavo nasleduje, dostane HTTP 412 (lebo hash je počítaný proti flox.* identite, ale server prijímajúci request očakáva hash voči primary doméne — nemá tieto hodnoty).4. ⚠ HTTP/1.1 — kritická požiadavka
BiznisWeb partner authentication nefunguje cez HTTP/2. Každý partner request musí byť poslaný cez HTTP/1.1.
Toto je menej zrejmé než sa zdá. Moderné HTTP knižnice často automaticky negotiate HTTP/2 cez ALPN, ak server podporuje (BiznisWeb server podporuje, ale partner middleware funguje len cez HTTP/1.1).
| Klient | Default verzia | Ako vynútiť HTTP/1.1 |
|---|---|---|
Python requests |
HTTP/1.1 ✓ | Automaticky |
Python httpx |
HTTP/1.1 (default) alebo HTTP/2 (s http2=True) |
httpx.Client(http2=False) |
Python aiohttp |
HTTP/1.1 ✓ | Automaticky |
Node.js fetch / undici |
HTTP/1.1 | Automaticky |
Node.js node-fetch |
HTTP/1.1 | Automaticky |
Go net/http |
HTTP/2 negotiated via ALPN | tr.ForceAttemptHTTP2 = false alebo nastavte tr.TLSNextProto = map[string]func(...) http.RoundTripper{} |
curl |
HTTP/2 cez TLS (default v moderných verziách) | curl --http1.1 ... |
PHP cURL |
Závisí od verzie libcurl | CURLOPT_HTTP_VERSION = CURL_HTTP_VERSION_1_1 |
Diagnostika: ak vidíte HTTP/2 412 v debug výstupe, klient sa pripojil cez h2 a partner auth neprešla. Vynúťte HTTP/1.1.
5. Časté gotchas
5.1 Snap (staging) eshopy — strip prefixu z hash inputu
BiznisWeb používa „snap" eshopy ako staging / shadow kópie produkčných webov pre testovanie. Ich hostname má tvar:
snap<digits>-<production-host>.flox.sk
Napríklad: snap1234-eshop1.flox.sk je staging kópia eshop1.flox.sk (resp. www.eshop1.sk v produkcii).
Partner registrácia žije na produkčnej identite (
eshop1.flox.sk), nie na snap hostname. To znamená:
- HTTP endpoint:
https://snap1234-eshop1.flox.sk/api/graphql(snap host) - Hash input:
"eshop1.flox.sk"+ secret + partner_id (produkčný host, BEZsnap1234-prefixu)
snap<digits>- prefix z hostname pred SHA1 výpočtom, ale request posielajte na snap hostname normálne.Príklad (Python)
import hashlib
import re
snap_pattern = re.compile(r"^snap\d+-")
def hash_input_domain(client_domain: str) -> str:
"""Strip 'snap<digits>-' prefix for hash computation."""
return snap_pattern.sub("", client_domain)
# Endpoint URL — snap hostname
endpoint = "https://snap1234-eshop1.flox.sk/api/graphql"
# Hash input — production identity (snap prefix stripped)
hash_input = hash_input_domain("snap1234-eshop1.flox.sk")
# → "eshop1.flox.sk"
partner_hash = hashlib.sha1(
(hash_input + partner_secret + partner_id).encode("utf-8")
).hexdigest()
Ak na snap eshope vidíte HTTP 401 + generická chyba „Stalo sa niečo neočakávané" namiesto očakávaného 412, prvá vec na overenie: počítate hash bez snap prefixu?
Ak produkčný eshop a snap kópia zdieľajú rovnaký API kľúč (najmä keď klient vytvoril token pred existenciou snapu, alebo skopíroval credentials), jediným rozdielom medzi úpravou produkčných a snap dát je endpoint URL:
https://eshop1.flox.sk/api/graphql→ modifikuje PRODUKČNÝ eshop (živých zákazníkov)https://snap1234-eshop1.flox.sk/api/graphql→ modifikuje snap (staging kópiu)
Host hlavičky requestu.Vždy explicitne overte cieľ pred mutáciou.
5.2 HTTP 308 redirect z flox.* na vlastnú doménu
Eshopy s vlastnou doménou majú zvyčajne *.flox.sk subdomain nastavenú ako permanent redirect (HTTP 308) na primárnu doménu. Napr.:
https://eshop1.flox.sk/api/graphql
→ HTTP 308 Permanent Redirect
→ Location: https://www.eshop1.sk/api/graphql
Ak Váš klient followuje redirecty automaticky (defaultné správanie väčšiny knižníc), request prejde na www.eshop1.sk. Server tam ale očakáva hash voči jeho doméne — a Váš hash bol počítaný proti eshop1.flox.sk alebo voči www.eshop1.sk (čo nie je client domain). Výsledok: HTTP 412.
- Posielajte request priamo na primary doménu (
https://www.eshop1.sk/api/graphql) — žiadny redirect, žiadny problém. - Hash vždy počítajte proti client domain (
eshop1.flox.sk), bez ohľadu na to, kde končí HTTP request. - Vypnite automatic redirect following (
allow_redirects=Falsev Python requests) — ak vidíte 3xx, je to signál, že máte zlú endpoint domain hodnotu.
5.3 IP whitelist
BiznisWeb best practices odporúčajú obmedziť každý API token na konkrétne IP adresy (klient ho nastavuje v admin paneli pri vytváraní tokenu). Ak request prichádza z neallowed IP:
- Webový token (
BW-API-Key) nepustí vôbec → HTTP 401 - Partner token (
BW-Partner-Key) nepustí → HTTP 412
Pre vývoj môže klient ponechať whitelist prázdny (=povolené všetko). Pre produkčné integrácie odporúčame whitelist mať.
5.4 Aktivácia Partner Package
Klient musí mať na svojom eshope aktivovaný „Partner Package" — to mu povoľuje partner-specific volania (advanced filters, content mutations, content pages atď.). Bez tejto aktivácie aj správny partner token vráti HTTP 412 so správou „You can use the filter param with only valid partner token or your website must have Partner package!".
Klient si aktiváciu objednáva v BiznisWebe. Ako vývojár na to nemáte dosah — môžete len rozpoznať túto chybu a poslať klienta na podporu.
5.5 Limity frekvencie volaní BiznisWeb API
BiznisWeb API využíva pri spracovaní požiadaviek databázové pripojenia zo zdieľaného connection poolu. Pri príliš rýchlom odosielaní požiadaviek za sebou môže dôjsť k vyčerpaniu tohto poolu, čo sa prejaví chybovou odpoveďou namiesto štandardného JSON výstupu. Nejde o klasický rate limit v zmysle pevného počtu volaní za sekundu — server voľné kapacity priebežne uvoľňuje, preto je kľúčovým parametrom dĺžka pauzy medzi jednotlivými volaniami, nie ich celkový počet.
Na základe testovania odporúčame dodržiavať pri stránkovaných (paginovaných) požiadavkách minimálnu pauzu 0,5 sekundy medzi každým volaním. Táto hodnota poskytuje dostatočný bezpečnostný margin a pri bežných objemoch dát (tisíce až desaťtisíce záznamov) umožňuje plynulé stiahnutie bez výpadkov. Pri dlhodobých stiahnutiach presahujúcich 200 po sebe idúcich volaní odporúčame po každých 100 volaniach zaradiť krátku pauzu v trvaní 5 sekúnd, ktorá serveru umožní uvoľniť databázové pripojenia pred ďalšou dávkou požiadaviek.
- Minimálna pauza medzi volaniami: 0,5 s
- Maximálny počet volaní pred dlhšou pauzou: 100
- Dlhšia pauza po každých 100 volaniach: 5 s
6. HTTP error codes — diagnostika
| HTTP code | Význam | Pravdepodobná príčina | Čo robiť |
|---|---|---|---|
| 200 | OK | Request úspešný (GraphQL errors môžu byť v body) | Skontrolujte errors pole v response |
| 206 | Partial Content | Časť požadovaných polí token nemôže vrátiť (obmedzené práva) | Skontrolujte scope tokenu v admine |
| 308 | Permanent Redirect | Posielate na flox.* subdomain, eshop má vlastnú doménu | Použite primary doménu v API endpoint domain (hash ponechajte proti flox.*) |
| 401 | Unauthorized | Webový token (BW-API-Key) je nesprávny, expirovaný, alebo IP nie je v whitelist |
Klient nech overí / vygeneruje nový token v admin → Nastavenia → BiznisWeb API |
| 403 | Forbidden | Token autentifikoval, ale nemá scope na túto operáciu | Klient nech rozšíri token permissions |
| 404 | Not Found | Zlý endpoint URL alebo zlá doména | Skontrolujte https://<api-endpoint-domain>/api/graphql |
| 412 | Precondition Failed | Partner token (BW-Partner-Key) je neplatný, expirovaný, IP whitelist, alebo eshop nemá aktivovaný Partner Package |
Skontrolujte hash (SHA1, nie MD5; client domain, nie endpoint domain); IP whitelist; klient nech overí Partner Package; HTTP verzia (1.1!) |
| 429 | Too Many Requests | Rate limit prekročený | Rešpektujte Retry-After header, throttle |
| 500 | Server Error | Internal error na BW strane | Retry s backoff; ak persistent, kontaktujte BW support |
| 501 | Not Implemented / Maintenance | Údržba | Rešpektujte Retry-After, retry neskôr |
| 509 | Bandwidth Limit / Quota Exceeded | API quota vyčerpaná | Klient nech overí quota / upgrade plán |
7. Code samples
Všetky vzorky používajú rovnaké demo údaje pre čitateľnosť (hodnoty sú ilustračné):
domain = "www.eshop1.sk"
client_domain = "eshop1.flox.sk"
partner_id = "Demo-Integrator-2026"
partner_secret = "<your-partner-secret>"
api_key = "<your-32-char-api-token>"
7.1 Python (requests)
import hashlib
import requests
DOMAIN = "www.eshop1.sk"
CLIENT_DOMAIN = "eshop1.flox.sk"
PARTNER_ID = "Demo-Integrator-2026"
PARTNER_SECRET = ""
API_KEY = ""
def make_headers():
partner_hash = hashlib.sha1(
(CLIENT_DOMAIN + PARTNER_SECRET + PARTNER_ID).encode("utf-8")
).hexdigest()
return {
"BW-API-Key": f"Token {API_KEY}",
"BW-Partner-Key": f"{PARTNER_ID} {partner_hash}",
"Content-Type": "application/json",
}
def call(query: str, variables: dict | None = None) -> dict:
response = requests.post(
f"https://{DOMAIN}/api/graphql",
headers=make_headers(),
json={"query": query, "variables": variables or {}},
timeout=30,
# Critical: don't auto-follow redirects (would mask domain config issues)
allow_redirects=False,
)
if response.status_code == 308:
raise RuntimeError(
f"Server redirected to {response.headers.get('Location')}. "
f"Update DOMAIN to the redirect target (but keep CLIENT_DOMAIN as flox.*)."
)
response.raise_for_status()
return response.json()
# Example: partner-only advanced filter on getOrderList
result = call(
"""
query Orders($params: OrderParams, $filter: OrderFilter) {
getOrderList(lang_code: "SK", params: $params, filter: $filter) {
pageInfo { hasNextPage nextCursor }
data { order_num pur_date sum { value } }
}
}
""",
{
"params": {"limit": 30, "cursor": 0},
"filter": {"pur_date_from": "2026-01-01"},
},
)
print(result["data"]["getOrderList"]["data"])
7.2 Node.js (fetch)
import crypto from "node:crypto";
const DOMAIN = "www.eshop1.sk";
const CLIENT_DOMAIN = "eshop1.flox.sk";
const PARTNER_ID = "Demo-Integrator-2026";
const PARTNER_SECRET = "";
const API_KEY = "";
function makeHeaders() {
const partnerHash = crypto
.createHash("sha1")
.update(CLIENT_DOMAIN + PARTNER_SECRET + PARTNER_ID)
.digest("hex");
return {
"BW-API-Key": `Token ${API_KEY}`,
"BW-Partner-Key": `${PARTNER_ID} ${partnerHash}`,
"Content-Type": "application/json",
};
}
async function call(query, variables = {}) {
const response = await fetch(`https://${DOMAIN}/api/graphql`, {
method: "POST",
headers: makeHeaders(),
body: JSON.stringify({ query, variables }),
redirect: "manual", // catch 3xx so domain config issues don't get masked
});
if (response.status === 308) {
const location = response.headers.get("location");
throw new Error(
`Redirect to ${location}. Update DOMAIN to the redirect target.`
);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
return await response.json();
}
const result = await call(
`query Orders($params: OrderParams, $filter: OrderFilter) {
getOrderList(lang_code: "SK", params: $params, filter: $filter) {
data { order_num pur_date sum { value } }
}
}`,
{ params: { limit: 30 }, filter: { pur_date_from: "2026-01-01" } }
);
console.log(result.data.getOrderList.data);
7.3 PHP (cURL)
<?php
$domain = "www.eshop1.sk";
$client_domain = "eshop1.flox.sk";
$partner_id = "Demo-Integrator-2026";
$partner_secret = "";
$api_key = "";
$partner_hash = sha1($client_domain . $partner_secret . $partner_id);
$headers = [
"BW-API-Key: Token {$api_key}",
"BW-Partner-Key: {$partner_id} {$partner_hash}",
"Content-Type: application/json",
];
$query = 'query { getOrderList(lang_code: "SK", params: {limit: 30}, filter: {pur_date_from: "2026-01-01"}) { data { order_num } } }';
$payload = json_encode(["query" => $query, "variables" => new stdClass()]);
$ch = curl_init("https://{$domain}/api/graphql");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, // critical
CURLOPT_FOLLOWLOCATION => false, // detect 308 redirects
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 308) {
throw new RuntimeException("Server redirected — update \$domain to primary domain.");
}
if ($http_code !== 200) {
throw new RuntimeException("HTTP {$http_code}: {$response}");
}
$data = json_decode($response, true);
print_r($data["data"]["getOrderList"]["data"]);
7.4 curl (pre debug)
# Spočítajte hash v shelli (placeholder secret nahraďte skutočnou hodnotou)
HASH=$(printf "%s" "eshop1.flox.skDemo-Integrator-2026" | shasum -a 1 | awk '{print $1}')
curl --http1.1 \
-X POST "https://www.eshop1.sk/api/graphql" \
-H "BW-API-Key: Token " \
-H "BW-Partner-Key: Demo-Integrator-2026 $HASH" \
-H "Content-Type: application/json" \
-d '{"query":"query { getOrderList(lang_code: \"SK\", params: {limit: 5}, filter: {pur_date_from: \"2026-01-01\"}) { data { order_num pur_date sum { formatted } } } }"}'
Ak chcete vidieť presné HTTP headery a verziu, pridajte
-v. Ak nevidíte HTTP/1.1 v hlavičkách, znamená to že curl prešiel cez HTTP/2 — pridajte --http1.1 explicitne.8. Best practices
- Obmedzte duplicitné volania. Cache statické dáta (kategórie, stati statusov, jazyky, meny). Tieto sa menia zriedka.
- Obmedzte tokeny na konkrétne IP adresy v admin paneli.
- Pravidelne mažte nepoužívané tokeny.
- Posielajte request na primary doménu eshopu, nie na
*.flox.sk. Šetríte HTTP 308 redirect a vyhýbate sa potenciálnemu mismatch hashu. - Vypnite automatic redirect following (
allow_redirects=False/redirect: "manual"). Ak vidíte 3xx, je to konfiguračný problém. - Vynúťte HTTP/1.1. Server nepodporuje partner auth cez HTTP/2.
- Pýtajte sa len na potrebné polia. GraphQL umožňuje field selection — využite to.
- Pre paged endpoints (
getOrderList,getProductList, ...) používajte limit ≤ 30. Vyššie hodnoty môžu spôsobiť timeout/partial responses. - Pri mutáciách žiadajte aj zmenené polia v response — overíte si tým správnosť operácie:
mutation { changeOrderStatus(order_num: "12345", status_id: 4) { order_num status { id name } <!-- ← potvrdenie zmeny --> } } - Loggujte HTTP code a partial response — ale nikdy nezapisujte raw token alebo partner secret do logov.
- Rate limiting: rešpektujte
Retry-Afterheader pri 429 / 501.
9. Troubleshooting checklist
Keď Vaša integrácia padá, postupujte zhora nadol:
| Symptóm | Skontrolujte |
|---|---|
| Nekonkrétny network error / DNS fail | Doména v URL je správna; máte internet; firewall na klientovej / serverovej IP |
| HTTP 308 | Posielate na *.flox.sk, eshop má primary doménu — použite tú |
| HTTP 401 | Webový token (BW-API-Key) — overiť: 32 znakov; Token prefix v hlavičke; IP whitelist v admine; token nie je expirovaný/revokovaný |
| HTTP 412 | Partner token — najpravdepodobnejšie príčiny v poradí:
|
| HTTP 429 / 509 | Rate / quota limit — throttle; rešpektujte Retry-After |
HTTP 200 s GraphQL errors array |
Auth prešla, ale query má syntaktickú alebo permission chybu — pozrite errors[].message |
| HTTP 206 Partial Content | Token nemá scope na všetky požadované polia — overiť token permissions v admin |
| Random 500 errors | Backend issue na BW strane — retry s exponential backoff, kontaktujte BW support pri persistentnom probléme |