API
Přes API čtete konverzace, kontakty, reklamace a statistiky, zapisujete katalog a znalosti a děláte to, co tlačítka v panelu.
API klíče
V panelu otevřete Nastavení, část Propojení, řádek API a klikněte na Vytvořit klíč. Klíč pojmenujte podle systému, který ho bude používat, a vyberte, co smí. Klíč posílejte v každém požadavku v hlavičce Authorization: Bearer <klíč>.

| Oprávnění | Co klíč smí |
|---|---|
read | Číst konverzace, kontakty, reklamace, nejisté otázky, produkty, znalosti a statistiky. Všechny GET kromě webhooků. |
write | Akce jako tlačítka v panelu a přidávání a mazání znalostí. |
catalogue | Zapisovat a mazat produkty v katalogu. |
webhooks | Zobrazit doručení webhooků, poslat je znovu a poslat zkušební událost. |
Každý systém má mít vlastní klíč jen s tím, co potřebuje. Účetnictví stačí read, skladu catalogue. Které oprávnění adresa potřebuje, uvidíte u každé v části Reference API. Bez něj vrátí 403 s errcode insufficient_scope.
API a webhooky jsou součástí plánu Na míru. Zápis katalogu přes API funguje na každém plánu.
První požadavek
curl https://ottoai.sk/api/v1/me \
-H "Authorization: Bearer $OTTO_API_KEY"const r = await fetch("https://ottoai.sk/api/v1/conversations?status=human&limit=20", {
headers: { Authorization: "Bearer " + process.env.OTTO_API_KEY },
});
if (!r.ok) throw new Error((await r.json()).errcode);
const { conversations, nextBefore } = await r.json();$ch = curl_init("https://ottoai.sk/api/v1/leads?since=2026-10-01T00:00:00Z");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("OTTO_API_KEY")],
CURLOPT_RETURNTRANSFER => true,
]);
$kontakty = json_decode(curl_exec($ch), true)["leads"];Formát
- Všechny odpovědi jsou JSON v UTF-8.
- Časy jsou ISO 8601 v UTC, například
2026-10-10T08:00:00.000Z. - Id konverzací a kontaktů jsou UUID, id reklamací a nejistých otázek jsou čísla v řetězci.
- Celá specifikace je v OpenAPI 3.1, načtete ji do Postmanu, Insomnie i do generátoru klienta. Přehled adres je v části Reference API.
Stránkování
Seznamy vracejí nextBefore. Pošlete ho zpět jako before=, dokud není null. Je to neprůhledný řetězec, neskládejte ho sami. Počet řádků na straně vám konec neřekne.
let pred = null;
do {
const r = await fetch("https://ottoai.sk/api/v1/leads?limit=100" + (pred ? "&before=" + pred : ""), { headers });
const d = await r.json();
for (const kontakt of d.leads) zpracuj(kontakt);
pred = d.nextBefore;
} while (pred);Filtry
sinceauntilomezí čas vzniku (od, do bez něj),updatedSincečas poslední změny. Berou ISO 8601, špatný čas vrátí 400.- Konverzace:
status(bot,human,resolved),outcomealang. - Reklamace:
status(new,approved,rejected,resolved) akind(claim,withdrawal). limitje u konverzací, kontaktů, reklamací a otázek nejvýš 100 (výchozí 50), u produktů a znalostí 200 (výchozí 100).
Chyby
Každá chyba má { error, errcode, requestId }. errcode je stabilní a anglicky, podle něj se rozhodujte v kódu. error je věta pro člověka. U špatného parametru přibude errors s přesnou cestou.
{
"error": "Neplatný parameter.",
"errcode": "invalid_param",
"errors": [{ "path": "status", "message": "Použite bot, human alebo resolved." }],
"requestId": "1f0c6a7e-3b9a-4f0e-9c1d-2a7b8e5d4c3f"
}| Kód | Význam |
|---|---|
400 | Chybný požadavek, errors řekne co přesně. |
401 | Neplatný nebo chybějící klíč. |
403 | Funkce není ve vašem plánu (api_not_in_plan, webhooks_not_in_plan) nebo klíč nemá oprávnění (insufficient_scope). |
404 | Záznam neexistuje nebo není váš. Cizí záznamy vracejí 404, ne 403. |
409 | Akce v tomto stavu nejde, třeba převzít vyřešenou konverzaci (conv_resolved). |
429 | Příliš mnoho požadavků, hlavička Retry-After řekne, za kolik sekund to zkusit znovu. |
Texty v error jsou zatím slovensky. Každá odpověď má hlavičku x-request-id. Když pošlete vlastní, vrátíme ji a uvidíme ji i my v záznamech.
Limity
Limit je 120 požadavků za minutu na firmu. Hlavičky RateLimit-Limit, RateLimit-Remaining a RateLimit-Reset v každé odpovědi řeknou, kolik zbývá a za kolik sekund se limit uvolní. Celý katalog přes snapshot pošlete nejvýš 12× za hodinu.
Akce
To, co dělají tlačítka v panelu, umí i API. Akce posílají stejné události webhooků jako panel.
| Adresa | Co dělá |
|---|---|
POST /conversations/<id>/messages | Odpoví zákazníkovi jako kolega. Otto v konverzaci zmlkne, zákazník mimo web dostane odpověď e-mailem. |
POST /conversations/<id>/notes | Interní poznámka, zákazník ji nevidí. |
POST /conversations/<id>/takeover | Převezme konverzaci. Bez operatorId zůstane dosavadní přiřazení. |
POST /conversations/<id>/release | Vrátí konverzaci Ottovi. |
POST /conversations/<id>/resolve, /reopen | Vyřeší konverzaci, případně ji otevře znovu. |
PATCH /leads/<id> | Označí kontakt jako vyřízený: { "handled": true }. |
PATCH /returns/<id> | Rozhodne o reklamaci, notify: true pošle zákazníkovi e-mail. |
POST /unanswered/<id>/dismiss | Skryje nejistou otázku ze seznamu. |
curl -X POST https://ottoai.sk/api/v1/conversations/$ID/messages \
-H "Authorization: Bearer $OTTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "Dobrý den, objednávka odešla dnes ráno.", "operatorId": "$OPERATOR" }'Id kolegů najdete v GET /api/v1/operators. Bez operatorId se jako autor v panelu ukáže API.
Výsledek konverzace a statistiky
Každá konverzace má outcome:
resolved_by_otto: Otto ji vyřídil sám.unanswered: Otto sám, ale s nejistou odpovědí.handed_off: odepsal kolega.missed: zákazník chtěl člověka a nikdo mu neodepsal.
GET /api/v1/stats vrátí počty za období i řadu po dnech, týdnech nebo měsících ve zvoleném časovém pásmu:
{
"conversationsTotal": 1824,
"conversationsMonth": 212,
"products": 236,
"range": {
"since": "2026-10-01T00:00:00.000Z",
"until": "2026-10-10T16:00:00.000Z",
"interval": "week",
"timezone": "Europe/Bratislava",
"totals": { "conversations": 64, "resolvedByOtto": 49, "unanswered": 6, "handedOff": 8, "missed": 1,
"ratingUp": 11, "ratingDown": 1, "leads": 7, "returns": 2 },
"series": [
{ "date": "2026-09-28", "conversations": 31, "resolvedByOtto": 24, "unanswered": 3, "handedOff": 4, "missed": 0, "ratingUp": 6, "ratingDown": 0 },
{ "date": "2026-10-05", "conversations": 33, "resolvedByOtto": 25, "unanswered": 3, "handedOff": 4, "missed": 1, "ratingUp": 5, "ratingDown": 1 }
]
}
}
