Chybové odpovědi
Tvar chyby na REST API i na MCP, seznam kódů i s tím, kdy nastanou, a co má klient udělat s každým z nich. Kódy se generují z kódu, který je vrací.
Chyba REST API má vždycky stejný tvar a code je to, na co se má váš program rozhodovat. Text
v message je pro člověka, který se dívá do protokolu, a smí se kdykoli změnit. MCP server mluví
JSON-RPC, takže má tvar vlastní a je popsaný v samostatném oddíle níž.
{
"error": {
"code": "insufficient_scope",
"message": "This key is missing the `orders:read` scope. It has: products:read."
}
}Většina zpráv je anglicky, brány přístupu českyTělo chyby čte stroj a vývojář, proto je text u neplatného klíče, chybějícího oprávnění nebo neexistujícího záznamu anglicky. Výjimkou jsou odmítnutí, jejichž nápravu dělá obchodník, ne váš kód: plocha není zapnutá, obchod nemá placený tarif, přístup je pozastavený nebo dočasně vypnutý, nebo klíč patří k jiné ploše. Ta jsou česky, abyste je mohli obchodníkovi rovnou přeposlat. Rozhodujte se vždy podle code, ne podle textu.
Kódy
| Stav | Kód | Kdy nastane | Kde |
|---|---|---|---|
| 401 | invalid_api_key | Klíč neznáme, byl odvolaný, nebo mu vypršela platnost. Která z těch tří možností to je, schválně neprozrazujeme. | všechny cesty |
| 401 | missing_api_key | Hlavička s klíčem chybí, nebo hodnota nezačíná prefixem klíče. | všechny cesty |
| 403 | external_access_disabled | SHOPZI tuhle plochu (REST API nebo server MCP) dočasně vypnulo pro všechny obchody. Na obchodu ani na klíči nic neměňte a zkuste to později. | všechny cesty |
| 403 | external_access_not_enabled | Vlastník obchodu plochu nezapnul. Zapíná ji v Nastavení → API a MCP přepínačem REST API nebo Server MCP. | všechny cesty |
| 403 | external_access_suspended | SHOPZI externí přístup k tomuto obchodu pozastavilo. Důvod sdělí vlastníkovi obchodu podpora SHOPZI. | všechny cesty |
| 403 | insufficient_scope | Klíč je platný, ale nemá oprávnění, které cesta vyžaduje. Zpráva vypíše, co klíči chybí a co má. | všechny cesty |
| 403 | key_surface_mismatch | Klíč je vydaný jen pro druhou plochu (jen pro REST API, nebo jen pro MCP). Vlastník obchodu vydá klíč pro správnou plochu v Nastavení → API a MCP. | všechny cesty |
| 403 | plan_required | REST API i server MCP patří k placeným tarifům a obchod teď aktivní placený tarif nemá. Po obnovení tarifu se přístup otevře sám. | všechny cesty |
| 403 | shop_unavailable | Obchod je deaktivovaný nebo pozastavený, takže požadavky přes API ani MCP neobsluhuje. Klíč s tím nic nezmůže; otevře ho až obnovení obchodu. | všechny cesty |
| 404 | not_found | Záznam s tímhle identifikátorem v obchodě není. | všechny cesty |
| 404 | shop_not_found | Na doméně, na kterou požadavek přišel, žádný obchod neběží. API se volá na doméně obchodu. | všechny cesty |
| 429 | rate_limited | Klíč vyčerpal strop volání za minutu nebo za den podle tarifu obchodu. Počkejte tolik sekund, kolik říká hlavička Retry-After, a pokračujte. | všechny cesty |
| 500 | server_error | Chyba na naší straně. Opakujte s odstupem. | všechny cesty |
| 400 | invalid_cursor | Kurzor je poškozený, nebo patří k jinému dotazu (jiná cesta, jiný filtr nebo jiné updated_since). Začněte průchod znovu bez cursor. | všechny výpisy |
| 400 | invalid_query | Parametr dotazu má neplatnou hodnotu: čas v updated_since bez časového posunu, nebo hodnota filtru, kterou výčet nezná. Zpráva jmenuje parametr, u filtru i povolené hodnoty. | všechny výpisy |
| 402 | plan_required | Tarif obchodu feedy dodavatelů nezahrnuje, takže katalog dodavatelů se neotevře. Otevře ho tarif Basic a vyšší; ostatní cesty feedů vracejí, co obchod už má uložené. | GET /api/v1/catalog-items |
| 403 | inventory_disabled | Obchod má vypnutý modul Sklad, takže skladová data neotevírá nikomu. | GET /api/v1/stock GET /api/v1/stock/movements |
| 422 | isdoc_invalid | Doklad by neprošel pravidly ISDOC, typicky dobropis bez čísla opravovaného dokladu. Zpráva říká proč; opakování nepomůže, doklad je potřeba opravit v administraci. | GET /api/v1/documents/{id}/isdoc |
| 422 | unsupported_document_type | Doklad existuje, ale daňový není: objednávka, cenová nabídka ani účtenka nemají rozpis DPH, PDF faktury ani ISDOC. | GET /api/v1/documents/{id} GET /api/v1/documents/{id}/isdoc GET /api/v1/documents/{id}/pdf |
| 502 | pdf_unavailable | PDF se teď nepodařilo vyrobit. Nic se nezměnilo, zkuste to s odstupem znovu. | GET /api/v1/documents/{id}/pdf |
| 503 | pdf_busy | Obchod právě vyrábí jiná PDF a další vykreslení by čekalo ve frontě. Počkejte tolik sekund, kolik říká hlavička Retry-After, a zkuste to znovu. Doklad, jehož PDF už existuje, tuhle odpověď nedostane. | GET /api/v1/documents/{id}/pdf |
Kódů sdílených všemi cestami je 13. V jakém pořadí se kontroly přístupu vyhodnocují, popisuje článek Autentizace a oprávnění.
Co s nimi dělat
- 400 znamená, že cesta dotaz nepřečetla.
invalid_querypřijde u neplatné hodnoty parametru (čas bez časového posunu vupdated_since, hodnota filtru mimo výčet) a zpráva jmenuje parametr.invalid_cursorpřijde u kurzoru, který se cestou poškodil nebo patří k jinému dotazu, třeba s jiným filtrem. Stejný dotaz znovu neprojde; uinvalid_cursorzačněte průchod bezcursor. - 401 znamená, že klíč není platný nebo hlavička chybí.
missing_api_keyje požadavek úplně bez klíče (nebo s hodnotou, která nezačíná prefixemshopzi_sk_). Rozhoduje se o něm dřív než o přepínači plochy, takže přijde i na obchodě, který externí přístup zapnutý nemá; předběhne ho jen neexistující nebo pozastavený obchod a plocha dočasně vypnutá pro všechny obchody.invalid_api_keyznamená klíč, který se nepodařilo ověřit. Neopakujte: znovu poslaný stejný klíč bude neplatný pořád. Odpověď nese hlavičkuwww-authenticate, takže na ni umí zareagovat i obecný HTTP klient. - 403 znamená, že klíč je v pořádku, ale nedosáhne tam, kam sáhl. Buď mu chybí oprávnění (pak
ho vlastník obchodu klíči doplní v Nastavení → API a MCP ikonou Upravit
klíč), nebo je vypnutá celá funkce obchodu, jako u
inventory_disabled. Ani jedno neopravíte opakováním.- Čtyři kódy se týkají celé plochy, na kterou voláte, ne vašeho klíče. Přicházejí dřív, než se
ověří platnost klíče, takže je dostanete i se správným klíčem a naopak z nich nepoznáte,
jestli je klíč platný. Kontroluje se to v tomhle pořadí:
external_access_disabled: plocha je na SHOPZI dočasně vypnutá pro všechny obchody (údržba nebo zásah při incidentu). Zkuste to později.external_access_not_enabled: obchodu tahle plocha není zapnutá. Obchodník ji zapíná v administraci, viz Autentizace a oprávnění.external_access_suspended: SHOPZI externí přístup tomuto obchodu pozastavil. Obchodník se důvod dozví od podpory; opakování ani nový klíč nepomůže.plan_required: obchod nemá na externí přístup nárok. Nemá tedy ani aktivní placený tarif, ani licenci, ani zkušební období s uloženou kartou (je třeba na tarifu Free, nebo ve zkušebním období bez karty). Po zaplacení tarifu nebo po doplnění karty se přístup otevře sám, bez nového klíče.
key_surface_mismatch: klíč je omezený na druhou plochu (jen REST API, nebo jen server MCP). Pro tuhle plochu je potřeba jiný klíč.shop_unavailableznamená pozastavený nebo neaktivní obchod. Obvykle ho ale nedostanete: takový obchod odmítne dřív ochranná vrstva před API (viz oddíl Když odpověď nemá tvar chyby API níž).
- Čtyři kódy se týkají celé plochy, na kterou voláte, ne vašeho klíče. Přicházejí dřív, než se
ověří platnost klíče, takže je dostanete i se správným klíčem a naopak z nich nepoznáte,
jestli je klíč platný. Kontroluje se to v tomhle pořadí:
- 402
plan_requiredvrací jediná cesta,GET /api/v1/catalog-items: obchod externí přístup má, ale jeho tarif nezahrnuje feedy dodavatelů (počet odběrů feedů je v tarifu nula), takže katalog dodavatelů nevidí. Je to jiná situace než403 plan_requiredvýš, kdy obchod nemá nárok na celé API; rozlišujte je podle stavového kódu. Opakování nepomůže, katalog se otevře po přechodu na tarif s feedy dodavatelů. Ostatní cesty feedů dodavatelů tuhle chybu nevracejí, vydají to, co obchod má uložené (obchodu, který feedy nikdy neodebíral, prázdný výpis). - 404 u
not_foundznamená, že záznam v obchodě není; u dokladu i to, že je stornovaný, protože stornovaný dobropis se přes API nevydává. Ushop_not_foundznamená, že na doméně, na kterou jste volali, žádný obchod neběží: nejčastěji je to shopzi.cz místo domény obchodu. Podívejte se na hostitele v adrese, ne na klíč. - 422 vracejí cesty dokladu.
unsupported_document_typeznamená, že doklad existuje, ale daňový není (objednávka, cenová nabídka, účtenka), a nemá proto detail, PDF ani ISDOC.isdoc_invalidznamená, že by ISDOC neprošel pravidly formátu, typicky u dobropisu bez čísla opravovaného dokladu; zpráva říká proč. Ani jedno neopraví opakování. - 429
rate_limitedznamená, že klíč vyčerpal strop volání REST API za minutu nebo za den (oddíl Objem požadavků níž). Počkejte tolik sekund, kolik říká hlavičkaRetry-After, a pak pokračujte tam, kde jste skončili. Opakovat dřív nemá smysl: minutové okno je pevné a končí s celou minutou, takže každý další pokus před jejím koncem dostane znovu 429. - 500
server_errorje chyba na naší straně. Tady opakování smysl má, ale s odstupem, který se prodlužuje. - 502
pdf_unavailableznamená, že se PDF dokladu teď nepodařilo vyrobit. Nic se nezměnilo; opakujte s odstupem. - 503
pdf_busypřijde, když se PDF dokladu ještě nevyrábělo a server už vyrábí nejvíc souběžných PDF, kolik smí. Počkejte tolik sekund, kolik říká hlavičkaRetry-After, a zkuste to znovu. Stahujte PDF po jednom nebo po dvou, ne celou dávku naráz; doklad, jehož PDF už jednou vzniklo, přijde hned a tuhle odpověď nedostane.
Neplatný klíč, odvolaný klíč a vypršelý klíč vracejí totéžVšechny tři jsou 401 invalid_api_key se stejným textem. Rozlišovat je by znamenalo prozradit tomu, kdo klíče zkouší, že jeden z nich existoval. Který stav klíč má, je vidět v administraci v seznamu klíčů. Chybějící hlavička a hodnota bez prefixu mají jiný kód, missing_api_key: o těch se rozhoduje bez databáze.
Chyby přes MCP
MCP server má vlastní tvar, protože mluví JSON-RPC, a je užitečné rozlišit tři vrstvy.
Nepustila brána. Všechno, co se rozhodne na vstupu, přijde jako obyčejná HTTP odpověď v tvaru
chyby REST API výš, ne jako odmítnutí nástroje: 403 origin_not_allowed u požadavku s hlavičkou
Origin (prohlížečového klienta nepodporujeme, takže se odmítne každý původ), 401 u chybějícího
nebo neplatného klíče, 403 u zavřené plochy, chybějícího tarifu, pozastavení a klíče pro druhou
plochu, 404 shop_not_found na doméně bez obchodu a 405 method_not_allowed s hlavičkou
Allow: POST na GET a DELETE.
Nástroj odmítl. Přijde běžný úspěšný JSON-RPC výsledek s isError: true, českou větou
v content a strojovým kódem v _meta pod klíčem cz.shopzi/errorCode. structuredContent
v takové odpovědi nikdy není: výstupní schéma nástroje popisuje úspěch, takže chybový objekt
by na něm spadl a důvod odmítnutí by se k modelu vůbec nedostal. Kódy jsou forbidden,
not_found, conflict, invalid_input, suspended, plan_required, confirmation_required,
confirmation_declined, approval_required, approval_pending, credit_cap_exceeded,
plan_change_limited, quote_changed, payment_declined a internal_error.
confirmation_requiredaconfirmation_declinedznamenají „zeptej se člověka a zavolej znovu“, ne „na tohle nemáš právo“, a hostitel, který by je nerozlišil, by uživateli tvrdil, že mu chybí oprávnění, které mu nechybí.confirmation_requiredpřijde i tehdy, když schválení mezitím vypršelo nebo už bylo jednou použité: zavolejte nástroj znovu a vznikne nové.approval_requiredznamená, že akci musí schválit člověk v administraci obchodu. Odkaz na stránku schválení je v_meta["cz.shopzi/approvalUrl"]. Předejte ho uživateli a po schválení zavolejte nástroj znovu se stejnými argumenty.approval_pendingznamená, že se na schválení pořád čeká. Odkaz je tentýž, nové schválení nevzniklo; neopakujte volání ve smyčce, počkejte na člověka. Jeden klíč má na schválení nejvýš 20 čekajících žádostí a za hodinu jich založí nejvýš 30, ať skončily jakkoli; obojí je odmítnutíconflicts větou, na co počkat. Zamítnutá žádost vrátíconfirmation_declined.credit_cap_exceededznamená, že by akce překročila měsíční strop kreditů přístupového klíče (nový klíč má 2 000 kreditů měsíčně, pokud mu vlastník nenastaví jiný), nebo strop teď nejde ověřit. Nestrhlo se nic. Opakování pomůže až prvního dne dalšího měsíce, kdy se strop obnoví, nebo když vlastník obchodu strop klíče upraví v Nastavení → API a MCP ikonou Upravit klíč. Když je strop klíče nastavený na nulu, je utrácení pro ten klíč vypnuté a nový měsíc nepomůže. Když strop jen nešel ověřit, zkuste volání za chvíli znovu.plan_requiredznamená, že akci neumožňuje tarif obchodu. Vyčerpaný počet vlastních domén to není: ten mají všechny tarify stejný, takže přijdeconflicts větou, že je potřeba nejdřív odebrat stávající doménu.plan_change_limitedznamená, že se tarif obchodu přes MCP za posledních 24 hodin už měnil (nebo se právě mění). Nestrhlo se nic. Od kdy půjde další změna, je v_meta["cz.shopzi/retryAt"]; v administraci jde tarif změnit hned.quote_changedznamená, že potvrzená částka navýšení tarifu už neplatí: Stripe teď spočítal jinou, nabídka je starší než 90 minut, nebo patří k jiné periodě platby. Nestrhlo se nic. Zavolejteupgradeznovu bezquotea nechte potvrdit novou částku, starou nabídku neopakujte.payment_declinedznamená, že karta uložená u SHOPZI platbu za navýšení tarifu odmítla. Tarif zůstal a nic se nestrhlo. Se stejnou kartou opakování nepomůže; kartu obchodník vymění v administraci v Nastavení → Předplatné tlačítkem Spravovat platby, a tahle věta je i ve zprávě odmítnutí.suspendedje pojistka pro obchod, který se pozastavil během volání. Pozastavený obchod jinak neodpovídá už na vstupu (viz oddíl níž).internal_errorje nečekaná chyba na naší straně. Věta je obecná schválně: podrobnosti zůstávají v našem protokolu, aby se ven nedostaly názvy tabulek ani cizí data. Zkuste to znovu s odstupem.
Selhal protokol. Pak je to opravdová JSON-RPC chyba s číselným kódem: -32601 na neznámou
metodu (a na subscriptions/listen, které nevedeme, s HTTP 404), -32602 na neplatné parametry
(sem spadá i podvržený nebo pozměněný requestState z potvrzovacího kola), -32600 s HTTP 400 na
dávku, ve které je completion/complete nebo subscriptions/listen, -32021 na chybějící
schopnost klienta, -32022 na nepodporovanou verzi protokolu a -32020, když si hlavičky a tělo
požadavku odporují.
Chyba z databáze se ven nedostane nikdy v surové podobě: přeloží se na českou větu bez jmen tabulek a omezení, a surový text zůstane v našem protokolu.
Když odpověď nemá tvar chyby API
Dva případy, kdy odpověď nevypadá jako chyby výš:
- 503 s tělem
{ "error": "Tento obchod je momentálně nedostupný." }posílá ochranná vrstva ještě před API. Znamená pozastavený nebo neaktivní obchod, nebo subdoménu shopzi.cz, na které žádný obchod neběží. Nemá polecodea nemá smysl ji parsovat jako chybu API. - Odpověď, která není JSON, přišla od něčeho před aplikací: od ochranné vrstvy sítě, od proxy,
nebo je to stránka obchodu, protože požadavek netrefil cestu
/api/v1. Zkontrolujte adresu a hlavičky.
Kde chybu dohledat
Volání, které dojde až k obchodu, si obchod zapíše do protokolu bez obsahu: čas, klíč, plochu, operaci, výsledek s kódem chyby, stavový kód HTTP, dobu zpracování, kredity, IP adresu a klienta. Vlastník nebo manažer obchodu ho najde v Nastavení → API a MCP v části Protokol volání:
- Nahoře zvolte období a filtry Plocha, Výsledek a Klíč. Volání, u kterých se klíč ověřit nepodařilo, mají v řádku klíče „Bez platného klíče“.
- Ve sloupci Výsledek je, jak volání dopadlo (Úspěch, Chyba, Odmítnuto, Neověřený klíč, Bez oprávnění, Strop kreditů, Nad limit, Čeká na potvrzení, Čeká na schválení), a pod ním strojový kód chyby, tentýž, jaký dostal klient.
Protokol ukazuje nejvýš posledních 30 dní. Neúspěšných volání zapíše jen omezený počet (bez platného
klíče 60 za hodinu na obchod, s platným klíčem 1 000 za den na klíč a plochu); kolik jich za období
opravdu přišlo, ukazuje část Využití, kde se denní součty drží 90 dní. Odmítnutí hlavičkou
Origin a požadavky na doménu bez obchodu se nezapisují nikam, protože k nim žádný obchod nepatří.
Objem požadavků
REST API /api/v1 počítá volání na každý klíč zvlášť, v minutovém a v denním okně. Strop se
řídí tarifem obchodu:
| Tarif | Volání za minutu | Volání za den |
|---|---|---|
| Basic | 60 | 20 000 |
| Standard | 120 | 50 000 |
| Business | 240 | 100 000 |
| Enterprise a jednorázová licence | 300 | 200 000 |
Obě okna jsou pevná, ne klouzavá. Minutové začíná a končí s celou minutou, denní je kalendářní den v českém čase a obnoví se o půlnoci v Praze. Dva klíče téhož obchodu se počítají odděleně, takže rozběhnutá integrace nevyčerpá strop té druhé.
Počítají se jen volání, která prošla ověřením klíče, oprávnění, plochy a tarifu: tato odmítnutí
401 a 403 ze stropu neubírají (inventory_disabled ano, tu vrací až cesta sama). Každé
započítané volání jde do minutového okna, i to, které skončí 429; do denního jen ta, která
minutový strop pustil, takže klient, který chvíli nečekal, si tím nespálí zbytek dne.
Odpověď na volání, které prošlo všemi branami, tedy klíčem, plochou, tarifem i oprávněním, nese hlavičky, podle kterých si klient sám určí tempo; nese je i odmítnutí 429:
X-RateLimit-Limit: strop okna, které je právě těsnější (minutové, nebo denní);X-RateLimit-Remaining: kolik volání v tom okně ještě zbývá;X-RateLimit-Reset: kdy se to okno obnoví, jako Unix čas v sekundách.
Chybí jen ve výjimečné chvíli, kdy počítadlo nejde zapsat: volání pak projde bez omezení a hlavičky by neměly z čeho vzniknout.
Nad strop přijde 429 s kódem rate_limited a navíc hlavičkou Retry-After v sekundách. Když je
vyčerpaný celý den, Retry-After míří až na půlnoc, ne na konec minuty.
Příliš hustý provoz z jedné IP adresy může navíc odmítnout ochranná vrstva sítě ještě před aplikací; takové odmítnutí nemá tvar chyby API (viz oddíl výš). Držte se proto rozumné kadence a synchronizujte přírůstkově podle článku Stránkování a filtry, místo abyste stahovali celý katalog v každém běhu.
Volání nástrojů přes MCP mají strop také, ale jiný: každé volání nástroje se počítá klíči, kterým přišlo, v okně celé hodiny a kalendářního dne v českém čase, a strop se řídí tarifem obchodu:
| Tarif | Volání nástrojů za hodinu | Volání nástrojů za den |
|---|---|---|
| Basic | 600 | 5 000 |
| Standard | 1 200 | 10 000 |
| Business | 2 000 | 20 000 |
| Enterprise a jednorázová licence | 3 000 | 30 000 |
Do téhož stropu se počítá každé kolo, i to, které se jen ptá na potvrzení, a také našeptávání hodnot
(completion/complete); našeptávání nad strop neodmítne, jen vrátí prázdnou nabídku. Vedle toho má
obchod vlastní strop AI generování, do kterého se počítá i práce Agenta v administraci, a když je
vyčerpaný, odmítne i volání přes MCP. Odmítnutí je v obou případech odmítnutí nástroje s kódem
conflict a věta v něm říká, který ze dvou stropů narazil, aby šlo poznat rozběhnutou integraci od
vytíženého obchodu. Samo to pomine, takže tady opakování s odstupem smysl má.