Přeskočit na hlavní obsah

Chyby a jak je číst

Chybová odpověď API se pozná podle stavového kódu, ne podle těla: podle toho, co chybu vyvolalo, přijde mapa polí, objekt s kódem chyby, holý řetězec – nebo vůbec nic. I chybová odpověď s tělem má Content-Type: application/hal+json, takže podle hlavičky se úspěch od chyby poznat nedá.

Stavové kódy​

Přehled kódů, které API vrací.

KódVýznam
200OK – požadavek úspěšně zpracován
201Created – záznam vytvořen
204No Content – bez obsahu
400Bad Request – špatný formát nebo datová chyba
401Unauthorized – chybí nebo neplatí API klíč
402Payment Required – zdroj není v tarifu, nebo je vyčerpaná kvóta dotazů
403Forbidden – operaci nelze provést
404Not Found – záznam nenalezen
405Method Not Allowed – metoda není podporována
413Payload Too Large – nahrávaný soubor je nad limit
415Unsupported Media Type – nepodporovaný typ souboru
500Internal Server Error – neočekávaná chyba na straně iÚčta
501Not Implemented – zdroj tuto operaci neumí

Kódy 413 a 415 vrací jen nahrávání příloh, viz Přílohy. 405 vrací API na metody HEAD a PATCH, které nepodporuje.

Kód 429 Too Many Requests API nevrací nikdy – vyčerpaná kvóta dotazů se hlásí jako 402, viz Vyčerpaná kvóta.

Tvary chybového těla​

Který tvar přijde, závisí na tom, kde v aplikaci požadavek skončil – ne na stavovém kódu. Stejný kód 400 může přijít s několika různými těly.

TvarKdy vzniká
{"errors": {"pole": "hláška"}}neprošla validace vstupu
{"error_code": "…", "message": "…"}zamítnutý požadavek se strojově čitelným kódem
"Text hlášky" (holý JSON řetězec)ostatní zamítnutí
{"pole": ["hláška"]} (mapa bez obálky errors)validace parametrů seznamů a upload souborů, například {"date_from": ["Datum není platné."]}; vždy s kódem 400
["hláška"] (holé pole hlášek)validace akcí a parametrů stránkování, například ["Doklad je smazaný, nebo účetní období, či období DPH je uzavřeno."] nebo ["Maximální počet záznamů na stránku je '200'"] po ?pageSize=500; vždy s kódem 400

Schéma Error ve specifikaci pokrývá právě těchto pět tvarů.

Validace vstupu​

Vzniká, když neprojde validace polí při vytváření nebo úpravě záznamu. Klíče mapy jsou názvy polí tak, jak je posíláte v požadavku.

Hodnota je řetězec, ne vždy pole

Hláška se posílá jako holý řetězec, když je jediná, a jako pole až od dvou výš. Validace jednoho pole se navíc po první chybě přeruší, takže řetězec je běžný případ a pole výjimka. Kód, který rovnou iteruje (foreach ($body['errors'][$pole] as $msg)), spadne na většině odpovědí – ošetřete oba typy.

curl -i -X POST https://online.iucto.cz/api/1.3/invoice_issued \
-H "X-Auth-Key: $IUCTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_id": 1}'
{
"errors": {
"currency": "Měna není platná.",
"items": "Doklad musí mít alespoň jednu položku."
}
}

Chyba se strojově čitelným kódem​

Objekt s error_code a message. Jediný kód, který dnes API vrací, je TARIFF_MODULE_ACCESS_DENIED – dostane ho externí modul (propojení s bankou), jehož firma nemá tarif zahrnující daný modul.

{
"error_code": "TARIFF_MODULE_ACCESS_DENIED",
"message": "Modul není dostupný, firma nemá aktivní tarif zahrnující tento modul."
}

Holý řetězec​

Nejčastější tvar u zamítnutých požadavků. Tělo je platný JSON, ale je to řetězec, ne objekt – json_decode vrátí string.

curl -i -X POST https://online.iucto.cz/api/1.3/invoice_issued \
-H "X-Auth-Key: $IUCTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{'
"Neplatný JSON formát."

Jak poznat, který přišel​

Rozlišuje se po dekódování JSON, podle typu a podle přítomnosti klíčů. Pořadí podmínek je důležité – errors se kontroluje dřív než error_code.

$http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$body = json_decode($response, true);

if ($http < 400) {
// úspěch
} elseif ($body === null) {
// prázdné tělo – kód je jediná informace
$detail = null;
} elseif (is_string($body)) {
// holá hláška
$detail = $body;
} elseif (isset($body['errors'])) {
// validace: pole => hláška, nebo pole => seznam hlášek
$detail = array_map(
static fn ($msgs) => is_array($msgs) ? $msgs : [$msgs],
$body['errors'],
);
} elseif (isset($body['error_code'])) {
// strojově čitelný kód
$detail = $body['error_code'];
} else {
// mapa bez obálky `errors` nebo holé pole hlášek
$detail = $body;
}
Prázdné tělo

Odpovědi 401 a 404 tělo většinou nemají vůbec – server pošle jen hlavičky a stavový kód. json_decode('') vrátí null, na což musí být kód připravený. Prázdné tělo vrací i 204 No Content u úspěšného DELETE.

402 vs. 403​

Oba kódy znamenají „požadavek je v pořádku, ale neprovede se". Liší se důvodem a hlavně tím, co s tím může udělat integrace.

KódVýznamCo s tím
402Zdroj není v tarifu firmy, nebo je vyčerpaná kvóta dotazůOpakování nepomůže – do změny tarifu nebo do resetu kvóty bude odpověď stejná
403Operaci nelze provést – uzavřené období, vazba na jiný záznam, EET, chybějící oprávnění uživateleMusí se změnit stav dat nebo nastavení, ne požadavek

Zkráceně: 403 znamená „tohle nejde udělat", 402 „tohle nemáte zaplacené".

Časté příčiny 402​

SituaceTělo odpovědi
Zdroj vyžaduje vyšší tarif – sklady, skladové karty a skladové pohyby vyžadují tarif se skladovým modulem"Zdroj je dostupný pouze ve vyším tarifu."
Vyčerpaná kvóta dotazů, viz Limity a kvóty"Překročen limit na počet dotazů v API, zkontrolujte aktivní tarif. Dnešní využití: …, za měsíc: …"
Vytvoření, editace nebo zaúčtování dokladu při vyčerpaném limitu dokladů zkušebního tarifu"Využíváte verzi zdarma s limitem … dokladů. Chcete-li pokračovat, zvolte některou z placených variant provozu iÚčta."
Externí modul bez odpovídajícího tarifuobjekt s error_code: TARIFF_MODULE_ACCESS_DENIED

Časté příčiny 403​

SituaceHláškaCo s tím
Účetní období nebo období DPH je uzavřenoDoklad je smazaný, nebo účetní období, či období DPH je uzavřeno.Buď období otevřít, nebo účtovat do aktuálního. Pozor na rozdíl u zaúčtování, viz níž
Záznam je navázaný na jinýNelze smazat záznam.Zákazník na faktuře, faktura na platbě – nejdřív odstranit vazbu
Doklad má EETNelze upravit doklad, který byl odeslán do EET…Doklady odeslané do EET nejde editovat ani mazat, řeší se opravným daňovým dokladem
Uživatel nemá právo–Oprávnění v API kopírují oprávnění ve webovém rozhraní, viz Autentizace

Uzavřené období: 403 nebo 400​

Uzavřené účetní období a uzavřené období DPH jsou dva nezávislé zámky a podle operace vrací různý kód i různý tvar těla – 403 u editace a mazání, 400 u zaúčtování, přičemž text hlášky je v obou případech stejný. Celý výklad včetně tabulky operací je v Dva nezávislé zámky.

Větvěte podle kódu, ne podle textu​

Rozhodovací logika patří na stavový kód. Texty hlášek jsou české, nejsou součástí smlouvy API a mění se s aplikací – porovnávání řetězců je proto křehké a rozbije se bez varování.

  • Stavový kód je stabilní a strojově čitelný. Větvěte podle něj.
  • error_code je jediný strojově čitelný identifikátor v těle. Dnes má jedinou hodnotu, TARIFF_MODULE_ACCESS_DENIED.
  • Text hlášky patří do logu a do zprávy pro člověka, ne do podmínky.
  • Klíče mapy errors jsou názvy polí požadavku. Ty se dají použít k namapování chyby na konkrétní vstup ve vaší aplikaci. Nespoléhejte se ale na to, že klíč errors u kódu 400 vždy existuje – tvarů je pět.

Časté hlášky 400 a jejich příčina​

Hlášky ke kódům 402 a 403 jsou v Časté příčiny 402 a Časté příčiny 403 výše.

KódHláškaPříčina
400Komunikace musí probíhat přes protokol HTTPS.Požadavek šel na http://
400Neplatná verze API, nebo zdroj.Chybná verze nebo název zdroje v URL
400Tělo požadavku je prázdné.POST nebo PUT bez těla
400Neplatný JSON formát.Tělo se nepodařilo naparsovat
400Neplatný typ požadavku.multipart/form-data na zdroji, který nahrávání souborů neumí
400Parametr doctype je povinný. / není platný.Chybí nebo je neplatný doctype u typů DPH a typů účetních položek
400Parametr date je povinný. / není platný.Chybí nebo je neplatné date u sazeb DPH
400Seznam sazeb DPH je dostupný pouze pro země EUDotaz na sazby DPH pro zemi mimo EU
400Zaúčtovat lze pouze doklady z letošního roku…Zkušební tarif, zaúčtování dokladu z minulého roku
400Doklad je smazaný, nebo účetní období, či období DPH je uzavřeno.Zaúčtování dokladu v uzavřeném období (jako holé pole hlášek)

Kam dál​