Preskočiť na obsah
Pre vývojárov

Webhooky

Otto pošle na vašu adresu POST, keď sa niečo stane: nová konverzácia, odovzdanie, kontakt, reklamácia, načítaný katalóg. Podpis podľa Standard Webhooks.

Nastavenie

  1. V paneli otvorte Nastavenia, časť Prepojenia, riadok Webhooky.
  2. Zadajte verejnú adresu https:// vášho servera a vyberte udalosti. Keď necháte označené všetky, pošleme aj udalosti, ktoré pribudnú neskôr.
  3. Uložte. Panel ukáže podpisový kľúč whsec_…, uložte si ho k overeniu podpisu.
  4. Tlačidlom Poslať skúšobnú udalosť overíte spojenie hneď. Príde udalosť test.ping.
Nastavenie webhookov v paneli Otta s výberom udalostí
Webhooky v Nastaveniach: adresa a výber udalostí.

Udalosti

UdalosťKedy prídedata
conversation.startedZačala nová konverzácia
Keď zákazník napíše prvú správu.
{ conversationId, question }
conversation.handoffZákazník chce človeka
Otto nevie ďalej alebo zákazník vyslovene žiada človeka.
{ conversationId, question, confidence }
conversation.operator_joinedRozhovor prevzal človek
Niekto z tímu prevzal rozhovor v Schránke.
{ conversationId, operator }
conversation.resolvedKonverzácia je vyriešená
Označili ste ju v Schránke ako vyriešenú.
{ conversationId }
conversation.ratedZákazník ohodnotil konverzáciu
Palec hore alebo dole na konci konverzácie.
{ conversationId, rating }
message.created len na výberNová správa v konverzácii
Každá správa zákazníka, asistenta aj kolegu. Je ich veľa, zapnite ju, len keď ich naozaj spracujete.
{ id, conversationId, role, kind, text, author }
lead.createdZákazník zanechal kontakt
Meno, e-mail alebo telefón zadaný priamo v chate.
{ id, conversationId, name, email, phone }
lead.updatedKontakt je vybavený alebo znova otvorený
Niekto v paneli alebo cez API zmenil, či je kontakt vybavený.
{ id, conversationId, handled }
return.createdNová reklamácia alebo vrátenie
Zákazník odoslal formulár aj s prílohami.
{ id, kind, orderId }
return.updatedZmenil sa stav podnetu
Schválili ste ho, zamietli alebo vybavili.
{ id, kind, orderId, status }
unanswered.createdNeistá odpoveď
Otázka, na ktorú sa vo vašich znalostiach nenašla istá odpoveď.
{ conversationId, question }
catalog.syncedKatalóg sa načítal
Feed produktov sa načítal, príde aj počet produktov.
{ source, products }
catalog.failedKatalóg sa nenačítal
Feed produktov sa nepodarilo načítať. Príde pri prvom zlyhaní, nie pri každom ďalšom.
{ source, error }
usage.thresholdMíňa sa limit AI odpovedí
Príde pri 80 % a pri 100 % limitu AI odpovedí v pláne.
{ used, limit, level }

Udalosť message.created chodí len vtedy, keď ju zapnete sami. Je jej veľa a tlačidlo Označiť všetky ju vynechá.

Telo požiadavky

POST na vašu adresu
{
  "id": "1b6f6a3e-0f7c-4a51-9a3d-6a2f0e8c4b11",
  "event": "lead.created",
  "at": "2026-10-10T08:14:03.512Z",
  "apiVersion": "1",
  "tenantId": "3f6c2a10-8d4e-4c1b-a6f2-9e0d7b5c3a21",
  "tenant": "Váš obchod",
  "data": {
    "id": "8353028d-e582-4e08-8ed9-f882d200cd22",
    "conversationId": "37e7d86d-57bf-455f-9fc6-8c856a607d2f",
    "name": "Ján Novák",
    "email": "jan@example.sk",
    "phone": ""
  }
}

id je pri opakovaní rovnaké. Keď to isté id dorazí znova, záznam zahoďte.

Overenie podpisu

Podpisujeme podľa štandardu Standard Webhooks, overenie zvládne aj hotová knižnica pre Node.js, PHP, Python a ďalšie jazyky. Každá požiadavka nesie tri hlavičky:

webhook-idid udalosti, pri opakovaní rovnaké
webhook-timestampčas pokusu v sekundách od 1. 1. 1970
webhook-signaturev1,<podpis>, kde podpis je Base64 z HMAC-SHA256 nad id.timestamp.telo

Kľúčom HMAC je podpisový kľúč bez predpony whsec_, dekódovaný z Base64. Podpis počítajte zo surového tela, ešte pred parsovaním JSON. Pokus odmietnite, keď podpis nesedí alebo je čas starší ako 5 minút.

Node.js
import crypto from "node:crypto";

export function overPodpis(secret, hlavicky, suroveTelo) {
  const id = hlavicky["webhook-id"];
  const cas = hlavicky["webhook-timestamp"];
  const kluc = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const ocakavany = crypto.createHmac("sha256", kluc).update(id + "." + cas + "." + suroveTelo).digest("base64");
  const sedi = String(hlavicky["webhook-signature"] || "").split(" ").some((cast) => {
    const [verzia, podpis] = cast.split(",");
    return verzia === "v1" && podpis && podpis.length === ocakavany.length
      && crypto.timingSafeEqual(Buffer.from(podpis), Buffer.from(ocakavany));
  });
  return sedi && Math.abs(Date.now() / 1000 - Number(cas)) < 300;
}
PHP
function overPodpis(string $secret, array $h, string $suroveTelo): bool {
  $kluc = base64_decode(preg_replace('/^whsec_/', '', $secret));
  $sprava = $h['webhook-id'] . '.' . $h['webhook-timestamp'] . '.' . $suroveTelo;
  $ocakavany = base64_encode(hash_hmac('sha256', $sprava, $kluc, true));
  foreach (explode(' ', $h['webhook-signature'] ?? '') as $cast) {
    [$verzia, $podpis] = array_pad(explode(',', $cast, 2), 2, '');
    if ($verzia === 'v1' && hash_equals($ocakavany, $podpis)) {
      return abs(time() - (int) $h['webhook-timestamp']) < 300;
    }
  }
  return false;
}
Python
import base64, hashlib, hmac, time

def over_podpis(secret: str, h: dict, surove_telo: bytes) -> bool:
    kluc = base64.b64decode(secret.removeprefix("whsec_"))
    sprava = f"{h['webhook-id']}.{h['webhook-timestamp']}.".encode() + surove_telo
    ocakavany = base64.b64encode(hmac.new(kluc, sprava, hashlib.sha256).digest()).decode()
    sedi = any(
        hmac.compare_digest(cast.split(",", 1)[1], ocakavany)
        for cast in h.get("webhook-signature", "").split()
        if cast.startswith("v1,")
    )
    return sedi and abs(time.time() - int(h["webhook-timestamp"])) < 300

Pôvodnú hlavičku X-Otto-Signature: sha256=<HMAC_SHA256(secret, telo) v hex> posielame naďalej, staršie integrácie fungujú bez zmeny.

Opakovanie a vypnutie

Odpovedzte kódom 2xx do 5 sekúnd, prácu robte až potom. Keď odpoveď nepríde alebo nie je 2xx, skúsime to znova v týchto odstupoch: hneď, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h, 14 h, 24 h, spolu asi 3 dni. Udalosti čakajú v databáze, neprepadnú ani pri našom reštarte. Pri kóde 4xx okrem 408 a 429 neopakujeme.

Keď 3 dni nedoručíme ani jednu udalosť, webhook vypneme a pošleme vám e-mail. Zapnete ho tým, že ho v paneli znova uložíte.

Doručenia cez API