Partner API

Partner API umožňuje integračním partnerům přistupovat k privilegovaným funkcím BiznisWeb GraphQL API — pokročilé filtry, bulk operace, mutace obsahu, content pages, custom order info, tracking URLs a další operace, které nejsou dostupné přes běžný webový token.

Tento dokument je v2 a obsahuje zkušenosti z reálných integrací včetně gotcha, které nejsou v původní dokumentaci zřejmé. Pro rychlou referenci původní specifikace viz stránku Volání API.

1. Předpoklady

Před prvním voláním Partner API potřebujete:

ÚdajKde získatPříklad
Partner ID Od BiznisWebu po podpisu partnerské smlouvy Demo-Integrator-2026
Partner secret Od BiznisWebu spolu s Partner ID aB3$dEf... (řetězec, ~12–32 znaků)
Webový API token Klient (majitel eshopu) vygeneruje v admin panelu › Nastavení › BiznisWeb API; pošle Vám ho 32 znaků, např. Ab3xY9pQrSt2uVw5xYz8aB1cDeF4gH6i
Client domain BiznisWeb interní identifikátor eshopu — VŽDY ve tvaru <subdoména>.flox.cz nebo <subdoména>.flox.sk eshop1.flox.cz
API endpoint domain Veřejná doména, kde API skutečně poslouchá. Může být stejná jako client domain, nebo vlastní doména eshopu www.eshop1.cz
Pozor
API endpoint domain a Client domain se nemusí shodovat. Jsou to dvě různé hodnoty s různými účely:
  • API endpoint domain — kam posíláte HTTP request
  • Client domain — vstup do SHA1 hashe (BiznisWeb interní identita eshopu)
Pro eshopy, které běží přímo na *.flox.cz/*.flox.sk (žádná vlastní doména), jsou obě stejné. Pro eshopy s vlastní doménou (typicky CZ eshopy s .cz nebo SK eshopy s .sk) se liší. Viz sekci 3.

2. Autentizace — dva tokeny

Každý partnerský request musí obsahovat 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ý řetězec vygenerovaný klientem v admin panelu eshopu. Header value musí obsahovat prefix Token (s mezerou) před tokenem:

BW-API-Key: Token Ab3xY9pQrSt2uVw5xYz8aB1cDeF4gH6i
Nesprávně
Tyto formáty nefungují a vrátí HTTP 401:
X-Api-Key: Ab3xY...
Authorization: Bearer Ab3xY...
BW-API-Key: Ab3xY...   ← chybí "Token " prefix

2.2 Partner token REQUIRED

Header value je partner ID, mezera, a 40-znakový SHA1 hash:

BW-Partner-Key: Demo-Integrator-2026 d2f7e3a8b4c1e9f0a5b2c7d8e1f4a9b6c3d5e7a8
Nesprávně
BW-Partner-Key: Partner d2f7e3...     ← prefix musí být partner ID, ne slovo "Partner"
BW-Partner-Key: d2f7e3...             ← chybí partner ID prefix
BW-Partner-Key: Demo-Integrator-2026 raw_secret   ← musí to být HASH, ne sám secret

2.3 Výpočet partner hashe

Hash je SHA1 (ne MD5, ne SHA256) z konkatenace tří hodnot v přesném pořadí:

partner_hash = sha1(client_domain + partner_secret + partner_id)

Výstup je přesně 40 hex znaků. Pokud vidíte 32 znaků, použili jste MD5 — to je chyba.

VstupPopisPříklad
client_domain VŽDY *.flox.cz nebo *.flox.sk subdomain, ne veřejná doména eshopu eshop1.flox.cz
partner_secret Tajný klíč od BiznisWebu — používejte raw, bez prefixů prikladSecret***
partner_id Váš partner identifier Demo-Integrator-2026
Nejčastější chyba
Vývojáři často omylem použijí veřejnou doménu eshopu (www.eshop1.cz) místo client domain (eshop1.flox.cz). Hash pak nevaliduje — server vrací HTTP 412.

Pravidlo: client domain je vždy *.flox.cz/*.flox.sk subdomain, i když eshop má vlastní doménu.

Konkrétní příklad (Python)

import hashlib

client_domain = "eshop1.flox.cz"
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

Cílová URL pro HTTP POST je vždy:

https://<api-endpoint-domain>/api/graphql

Při partner volání existují dvě oddělené hodnoty:

PoleK čemuPříklad pro eshop s vlastní doménouPříklad pro eshop bez vlastní domény
API endpoint domain URL host kde API skutečně poslouchá www.eshop1.cz eshop2.flox.sk
Client domain Vstup do SHA1 hashe (interní BW identita) eshop1.flox.cz eshop2.flox.sk
Best practice
Vždy posílejte request na veřejnou doménu eshopu (pokud existuje), ne na *.flox.cz. Flox subdomain dělá HTTP 308 redirect na primary domain a klient, který redirect bezhlavě následuje, dostane HTTP 412 (protože hash je počítán proti flox.* identitě, ale server přijímající request očekává hash vůči primary doméně — nemá tyto hodnoty).

4. ⚠ HTTP/1.1 — kritický požadavek

Critical
BiznisWeb partner authentication nefunguje přes HTTP/2. Každý partner request musí být poslán přes HTTP/1.1.

To je méně zřejmé, než se zdá. Moderní HTTP knihovny často automaticky vyjednávají HTTP/2 přes ALPN, pokud server podporuje (BiznisWeb server podporuje, ale partner middleware funguje jen přes HTTP/1.1).

KlientDefault verzeJak vynutit HTTP/1.1
Python requests HTTP/1.1 ✓ Automaticky
Python httpx HTTP/1.1 (default) nebo 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 nebo nastavte tr.TLSNextProto = map[string]func(...) http.RoundTripper{}
curl HTTP/2 přes TLS (default v moderních verzích) curl --http1.1 ...
PHP cURL Závisí na verzi libcurl CURLOPT_HTTP_VERSION = CURL_HTTP_VERSION_1_1

Diagnostika: pokud vidíte HTTP/2 412 v debug výstupu, klient se připojil přes h2 a partner auth neprošla. Vynuťte HTTP/1.1.

5. Časté gotchas

5.1 Snap (staging) eshopy — strip prefixu z hash inputu

BiznisWeb používá „snap" eshopy jako staging / shadow kopie produkčních webů pro testování. Jejich hostname má tvar:

snap<digits>-<production-host>.flox.cz

Například: snap1234-eshop1.flox.cz je staging kopie eshop1.flox.cz (resp. www.eshop1.cz v produkci).

Gotcha
Partner registrace žije na produkční identitě (eshop1.flox.cz), ne na snap hostname. To znamená:
  • HTTP endpoint: https://snap1234-eshop1.flox.cz/api/graphql (snap host)
  • Hash input: "eshop1.flox.cz" + secret + partner_id (produkční host, BEZ snap1234- prefixu)
Jinými slovy: strippněte snap<digits>- prefix z hostname před SHA1 výpočtem, ale request posílejte na snap hostname normálně.

Pří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.cz/api/graphql"

# Hash input — production identity (snap prefix stripped)
hash_input = hash_input_domain("snap1234-eshop1.flox.cz")
# → "eshop1.flox.cz"

partner_hash = hashlib.sha1(
    (hash_input + partner_secret + partner_id).encode("utf-8")
).hexdigest()

Pokud na snap eshopu vidíte HTTP 401 + generická chyba „Stalo sa niečo neočakávané" místo očekávaného 412, první věc na ověření: počítáte hash bez snap prefixu?

⚠ Produkce vs snap — endpoint určuje cíl
Pokud produkční eshop a snap kopie sdílí stejný API klíč (zejména když klient vytvořil token před existencí snapu, nebo zkopíroval credentials), jediným rozdílem mezi úpravou produkčních a snap dat je endpoint URL:
  • https://eshop1.flox.cz/api/graphql → modifikuje PRODUKČNÍ eshop (živé zákazníky)
  • https://snap1234-eshop1.flox.cz/api/graphql → modifikuje snap (staging kopii)
Hash, partner token, API klíč — všechno může být stejné. Server rozhoduje výhradně podle Host hlavičky requestu.

Vždy explicitně ověřte cíl před mutací.

5.2 HTTP 308 redirect z flox.* na vlastní doménu

Eshopy s vlastní doménou mají obvykle *.flox.cz subdomain nastavenou jako permanent redirect (HTTP 308) na primární doménu. Např.:

https://eshop1.flox.cz/api/graphql
  → HTTP 308 Permanent Redirect
  → Location: https://www.eshop1.cz/api/graphql

Pokud Váš klient následuje redirecty automaticky (defaultní chování většiny knihoven), request přejde na www.eshop1.cz. Server tam ale očekává hash vůči jeho doméně — a Váš hash byl počítán proti eshop1.flox.cz nebo vůči www.eshop1.cz (což není client domain). Výsledek: HTTP 412.

Řešení
  1. Posílejte request přímo na primary doménu (https://www.eshop1.cz/api/graphql) — žádný redirect, žádný problém.
  2. Hash vždy počítejte proti client domain (eshop1.flox.cz), bez ohledu na to, kde končí HTTP request.
  3. Vypněte automatic redirect following (allow_redirects=False v Python requests) — pokud vidíte 3xx, je to signál, že máte špatnou endpoint domain hodnotu.

5.3 IP whitelist

BiznisWeb best practices doporučují omezit každý API token na konkrétní IP adresy (klient ho nastavuje v admin panelu při vytváření tokenu). Pokud request přichází z neallowed IP:

  • Webový token (BW-API-Key) nepustí vůbec → HTTP 401
  • Partner token (BW-Partner-Key) nepustí → HTTP 412

Pro vývoj může klient ponechat whitelist prázdný (=povoleno vše). Pro produkční integrace doporučujeme whitelist mít.

5.4 Aktivace Partner Package

Klient musí mít na svém eshopu aktivovaný „Partner Package" — to mu povoluje partner-specific volání (advanced filters, content mutations, content pages atd.). Bez této aktivace i správný partner token vrátí HTTP 412 se zprávou „You can use the filter param with only valid partner token or your website must have Partner package!".

Klient si aktivaci objednává v BiznisWebu. Jako vývojář na to nemáte dosah — můžete jen rozpoznat tuto chybu a poslat klienta na podporu.

5.5 Limity frekvence volání BiznisWeb API

BiznisWeb API využívá při zpracování požadavků databázová připojení ze sdíleného connection poolu. Při příliš rychlém odesílání požadavků za sebou může dojít k vyčerpání tohoto poolu, což se projeví chybovou odpovědí namísto standardního JSON výstupu. Nejde o klasický rate limit ve smyslu pevného počtu volání za sekundu — server volné kapacity průběžně uvolňuje, proto je klíčovým parametrem délka pauzy mezi jednotlivými voláními, nikoli jejich celkový počet.

Na základě testování doporučujeme při stránkovaných (paginovaných) požadavcích dodržovat minimální pauzu 0,5 sekundy mezi každým voláním. Tato hodnota poskytuje dostatečný bezpečnostní margin a při běžných objemech dat (tisíce až desítky tisíc záznamů) umožňuje plynulé stažení bez výpadků. Při dlouhodobých stahováních přesahujících 200 po sobě jdoucích volání doporučujeme po každých 100 voláních zařadit krátkou pauzu v délce 5 sekund, která serveru umožní uvolnit databázová připojení před další dávkou požadavků.

Doporučené hodnoty
  • Minimální pauza mezi voláními: 0,5 s
  • Maximální počet volání před delší pauzou: 100
  • Delší pauza po každých 100 voláních: 5 s

6. HTTP error codes — diagnostika

HTTP codeVýznamPravděpodobná příčinaCo dělat
200 OK Request úspěšný (GraphQL errors mohou být v body) Zkontrolujte errors pole v response
206 Partial Content Část požadovaných polí token nemůže vrátit (omezená práva) Zkontrolujte scope tokenu v adminu
308 Permanent Redirect Posíláte na flox.* subdomain, eshop má vlastní doménu Použijte primary doménu v API endpoint domain (hash ponechte proti flox.*)
401 Unauthorized Webový token (BW-API-Key) je nesprávný, expirovaný, nebo IP není ve whitelist Klient ať ověří / vygeneruje nový token v admin → Nastavení → BiznisWeb API
403 Forbidden Token autentifikoval, ale nemá scope na tuto operaci Klient ať rozšíří token permissions
404 Not Found Špatný endpoint URL nebo špatná doména Zkontrolujte https://<api-endpoint-domain>/api/graphql
412 Precondition Failed Partner token (BW-Partner-Key) je neplatný, expirovaný, IP whitelist, nebo eshop nemá aktivovaný Partner Package Zkontrolujte hash (SHA1, ne MD5; client domain, ne endpoint domain); IP whitelist; klient ať ověří Partner Package; HTTP verze (1.1!)
429 Too Many Requests Rate limit překročen Respektujte Retry-After header, throttle
500 Server Error Internal error na BW straně Retry s backoff; pokud persistentní, kontaktujte BW support
501 Not Implemented / Maintenance Údržba Respektujte Retry-After, retry později
509 Bandwidth Limit / Quota Exceeded API quota vyčerpaná Klient ať ověří quota / upgrade plán

7. Code samples

Všechny vzorky používají stejné demo údaje pro čitelnost (hodnoty jsou ilustrační):

domain         = "www.eshop1.cz"
client_domain  = "eshop1.flox.cz"
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.cz"
CLIENT_DOMAIN = "eshop1.flox.cz"
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,
        # Kritické: nesledovat redirecty automaticky (zamaskovalo by domain config issue)
        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()

# Příklad: partner-only advanced filter na getOrderList
result = call(
    """
    query Orders($params: OrderParams, $filter: OrderFilter) {
        getOrderList(lang_code: "CZ", 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.cz";
const CLIENT_DOMAIN = "eshop1.flox.cz";
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",  // zachytit 3xx aby domain config issues nebyly maskovány
  });
  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: "CZ", 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.cz";
$client_domain = "eshop1.flox.cz";
$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: "CZ", 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,  // kritické
    CURLOPT_FOLLOWLOCATION => false,                 // detekce 308 redirectů
    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 (pro debug)

# Spočítejte hash v shellu (placeholder secret nahraďte skutečnou hodnotou)
HASH=$(printf "%s" "eshop1.flox.czDemo-Integrator-2026" | shasum -a 1 | awk '{print $1}')

curl --http1.1 \
  -X POST "https://www.eshop1.cz/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: \"CZ\", params: {limit: 5}, filter: {pur_date_from: \"2026-01-01\"}) { data { order_num pur_date sum { formatted } } } }"}'
Pro debug
Pokud chcete vidět přesné HTTP headery a verzi, přidejte -v. Pokud nevidíte HTTP/1.1 v hlavičkách, znamená to že curl prošel přes HTTP/2 — přidejte --http1.1 explicitně.

8. Best practices

  1. Omezte duplicitní volání. Cache statická data (kategorie, stati statusů, jazyky, měny). Tato se mění zřídka.
  2. Omezte tokeny na konkrétní IP adresy v admin panelu.
  3. Pravidelně mažte nepoužívané tokeny.
  4. Posílejte request na primary doménu eshopu, ne na *.flox.cz. Šetříte HTTP 308 redirect a vyhýbáte se potenciálnímu mismatch hashe.
  5. Vypněte automatic redirect following (allow_redirects=False / redirect: "manual"). Pokud vidíte 3xx, je to konfigurační problém.
  6. Vynuťte HTTP/1.1. Server nepodporuje partner auth přes HTTP/2.
  7. Ptejte se jen na potřebná pole. GraphQL umožňuje field selection — využijte to.
  8. Pro paged endpoints (getOrderList, getProductList, ...) používejte limit ≤ 30. Vyšší hodnoty mohou způsobit timeout/partial responses.
  9. Při mutacích žádejte i změněná pole v response — ověříte si tím správnost operace:
    mutation {
      changeOrderStatus(order_num: "12345", status_id: 4) {
        order_num
        status { id name }   <!-- ← potvrzení změny -->
      }
    }
  10. Loggujte HTTP code a partial response — ale nikdy nezapisujte raw token nebo partner secret do logů.
  11. Rate limiting: respektujte Retry-After header při 429 / 501.

9. Troubleshooting checklist

Když Vaše integrace padá, postupujte shora dolů:

SymptomZkontrolujte
Nekonkrétní network error / DNS fail Doména v URL je správná; máte internet; firewall na klientové / serverové IP
HTTP 308 Posíláte na *.flox.cz, eshop má primary doménu — použijte ji
HTTP 401 Webový token (BW-API-Key) — ověřit: 32 znaků; Token prefix v hlavičce; IP whitelist v adminu; token není expirovaný/revokovaný
HTTP 412 Partner token — nejpravděpodobnější příčiny v pořadí:
  1. HTTP verze: vynuťte HTTP/1.1 (nejčastější příčina při první integraci)
  2. Client domain vs endpoint domain: hash musí být počítán proti flox.* identitě
  3. SHA1, ne MD5: hash musí mít 40 hex znaků (ne 32)
  4. Pořadí konkatenace: client_domain + secret + partner_id (v tomto pořadí, bez oddělovačů)
  5. Header format: <partner_id> <hash> (mezera mezi nimi; ne Partner <hash>)
  6. Snap eshop: pokud je URL snap<digits>-..., hash musí být počítán proti hostname BEZ snap prefixu
  7. Partner Package aktivní na klientovém eshopu (klient ať ověří v adminu)
  8. IP whitelist partner tokenu zahrnuje Vaši IP
HTTP 429 / 509 Rate / quota limit — throttle; respektujte Retry-After
HTTP 200 s GraphQL errors array Auth prošla, ale query má syntaktickou nebo permission chybu — viz errors[].message
HTTP 206 Partial Content Token nemá scope na všechna požadovaná pole — ověřit token permissions v admin
Random 500 errors Backend issue na BW straně — retry s exponential backoff, kontaktujte BW support při persistentním problému

Přihlášení

Vyzkoušejte web nebo e-shop zdarma

Zavřít
Předvolby soukromí

Soubory cookie používáme k vylepšení vaší návštěvy tohoto webu, k analýze jeho výkonu a ke shromažďování údajů o jeho používání. Můžeme k tomu použít nástroje a služby třetích stran a shromážděná data mohou být přenášena partnerům v EU, USA nebo jiných zemích. Soubory cookies ke zlepšení relevance reklam využívá služba Google Ads. Kliknutím na „Přijmout všechny soubory cookie“ vyjadřujete svůj souhlas s tímto zpracováním. Níže můžete najít podrobné informace nebo upravit své preference. 

Zásady ochrany soukromí

Ukázat podrobnosti