Centar za pomoć
Chat API

Webhooks

Poslednje ažuriranje:

Pregled vebhukova

Vebhukovi omogućavaju da ChatLab obavesti Vaše sisteme onog trenutka kada se nešto dogodi u Vašim četbotovima. Umesto periodičnog slanja upita ka Management API ili ručnog izvoza podataka, Vi registrujete HTTPS krajnju tačku (endpoint), a ChatLab šalje potpisan HTTP POST zahtev u realnom vremenu - kada posetilac ostavi lead, pošalje kontakt formu, oceni razgovor, zatraži ljudskog agenta ili kada se izvrši AI akcija.

Uobičajene primene:

  • prosleđivanje novih leadova direktno u Vaš CRM u sekundi kada su prikupljeni
  • obaveštavanje Vašeg tima na platformi Slack kada posetilac zatraži Live Chat (ćaskanje uživo)
  • slanje ocena i sažetaka razgovora u Vaš analitički sistem
  • praćenje izvršavanja AI akcija i slanje upozorenja u slučaju grešaka

Dostupnost: Vaš nalog mora sadržati funkciju Webhooks.

Gde se podešava: u administratorskoj aplikaciji otvorite Account settings -> Webhooks (Podešavanja naloga -> Vebhukovi; odmah pored odeljka za Management API). Vebhukovi funkcionišu na nivou celog naloga - jedna krajnja tačka može primati događaje iz svih Vaših botova ili iz jednog izabranog bota.

Podešavanje krajnje tačke

  1. Otvorite Account settings -> Webhooks i kliknite na Add endpoint (Dodaj krajnju tačku).
  2. Popunite obrazac za krajnju tačku:
    • Name (Naziv) - oznaka za Vašu ličnu referencu, npr. "CRM sinhronizacija" ili "Slack obaveštenja".
    • URL - HTTPS adresa na koju će ChatLab slati POST zahteve sa događajima.
    • Events (Događaji) - izaberite koje tipove događaja ova krajnja tačka prima (pogledajte katalog u nastavku). Izaberite samo ono što Vam je potrebno; događaji sa velikim obimom saobraćaja, poput ai_action.executed, mogu generisati mnogo mrežnog saobraćaja.
    • Bot filter (opciono) - izaberite jednog bota ili All bots (Svi botovi) za sve botove na Vašem nalogu.
    • Custom form filter (opciono) - usmerava prosleđivanje jedne prilagođene forme na ovu krajnju tačku. Ovo sužava samo događaj custom_form.submitted; svi ostali događaji na koje ste pretplaćeni (leadovi, zahtevi za kontakt, razgovori, Live Chat, AI akcije) biće isporučeni bez obzira na ovo podešavanje.
  3. Potvrdite unos. Tajni ključ (secret) krajnje tačke se prikazuje samo jednom u prozoru za potvrdu - kopirajte ga odmah i sačuvajte na bezbednom mestu. Biće Vam potreban za proveru potpisa (pogledajte odeljak Bezbednost u nastavku). Tekst ključa u čistom formatu ne može se naknadno preuzeti.

Svaka krajnja tačka takođe ima:

  • Prekidač za omogućavanje/onemogućavanje - pauzirajte isporuke bez brisanja krajnje tačke. Onemogućene krajnje tačke tiho odbacuju događaje (oni se ne stavljaju u red čekanja za kasnije).
  • Send sample event (Pošalji probni događaj) - šalje potpisan testni zahtev na Vaš URL kako biste mogli da proverite prijemnik od početka do kraja. Možete odabrati tip događaja i izmeniti probne vrednosti pre slanja, tako da Vaš program za obradu vidi realistične podatke. Test stiže kao redovna isporuka gde eventType odgovara Vašem izboru (ili kao webhook.test za jednostavnu proveru povezivanja).
  • Roll secret (Zameni tajni ključ) - generiše novi tajni ključ i poništava stari. Koristite ovo ako postoji sumnja da je ključ kompromitovan. Novi tajni ključ se ponovo prikazuje samo jednom. Za kontrolisanu zamenu ključa, pauzirajte krajnju tačku, zamenite i kopirajte novi tajni ključ, ažurirajte prijemnik, a zatim ponovo omogućite krajnju tačku i pošaljite probni događaj. Događaji nastali tokom pauze ne stavljaju se u red čekanja. Isplanirajte ovaj prekid pre zamene ključa.
  • Delivery log (Evidencija isporuka) - lista nedavnih isporuka za datu krajnju tačku sa vremenskom oznakom, tipom događaja, HTTP statusom koji je vratio Vaš server i vremenom odziva. Neuspele isporuke i pauze usled automatskog prekidača (circuit breaker) vidljive su ovde. Evidencija se čuva 14 dana.

Omotač događaja

Svaka isporuka je HTTP POST zahtev sa zaglavljem Content-Type: application/json. Telo zahteva uvek ima isti omotač (envelope); objekat data je specifičan za tip događaja:

{
  "eventId": "9f1c1c8e-6a2b-4b9e-9d2f-3f8a1e2b4c5d",
  "eventType": "lead.created",
  "timestamp": "2026-08-13T14:22:31Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": { }
}
  • eventId - jedinstven za svaki događaj. Koristite ga za deduplikaciju ukoliko Vaša obrada mora biti idempotentna.
  • eventType - jedan od tipova dokumentovanih u nastavku; takođe se šalje u zaglavlju X-ChatLab-Event.
  • timestamp - ISO 8601 UTC vreme kada je omotač isporuke pripremljen.
  • botId / botName - bot kome događaj pripada.
  • conversationId / sessionId - kontekst razgovora, kada je primenljivo.

Katalog događaja

lead.created

Aktivira se kada posetilac pošalje svoje kontakt podatke - putem obrasca za prikupljanje lidova, obrasca pre početka ćaskanja uživo ili prilagođenog obrasca koji se koristi za prikupljanje lidova.

{
  "eventId": "9f1c1c8e-6a2b-4b9e-9d2f-3f8a1e2b4c5d",
  "eventType": "lead.created",
  "timestamp": "2026-08-13T14:22:31Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "email": "jane.doe@example.com",
    "name": "Jane Doe",
    "phone": "+1 555 0123",
    "source": "LEAD_COLLECTION_FORM",
    "formCodeName": "lead_form",
    "formName": "Lead form",
    "fields": [
      {"name": "email", "value": "jane.doe@example.com", "type": "email"},
      {"name": "company", "value": "Acme Inc.", "type": "text"},
      {"name": "topics", "value": ["Billing", "Delivery"], "type": "multichoice"},
      {"name": "attachment", "value": "https://api.chatlab.com/aichat/customform/download?key=...&token=...", "type": "file"}
    ],
    "pageUrl": "https://acme.com/pricing"
  }
}
  • source - način na koji su kontakt podaci prikupljeni: LEAD_COLLECTION_FORM (obrazac za prikupljanje lidova), LIVE_CHAT_FORM (obrazac pre početka ćaskanja uživo), CONVERSATION (AI je preuzeo podatke tokom razgovora), ADMIN_DATA_UPDATE ili UPDATE_CLIENT_CONTEXT (uređeno na strani ChatLab-a). Slanje obrasca za podršku ljudskog operatera nikada ne aktivira ovaj događaj - umesto toga aktivira contact_form.submitted.
  • email, name, phone - kontakt podaci mapirani na zapis lida.
  • Kada se za prikupljanje lidova koristi prilagođeni obrazac, svako polje definisano u tom obrascu uključeno je u fields, redosledom iz obrasca, a formCodeName / formName identifikuju obrazac. Kod klasičnog obrasca za lidove oba su null, a fields je prazan niz.
  • Svaki unos u fields ima format {name, value, type}. name je tehnički naziv polja, nepromenljiv pri izmenama oznaka - koristite ga za mapiranje u vaš CRM.
  • Za polja tipa multichoice vrednost value je niz izabranih opcija. Polja sa poljem za potvrdu (checkbox) su pojedinačni unosi sa vrednostima "true" / "false".
  • Za polja tipa file vrednost value je link za preuzimanje otpremljene datoteke; webhook nikada ne prenosi sam sadržaj datoteke.
  • pageUrl - stranica na kojoj se posetilac nalazio u trenutku slanja.

contact_form.submitted

Aktivira se kada posetilac pošalje kontakt obrazac za podršku ljudskog operatera ili prilagođeni obrazac koji se koristi za kontakt sa čovekom.

{
  "eventId": "3a7b9c2d-1e4f-4a6b-8c0d-5e2f7a9b1c3d",
  "eventType": "contact_form.submitted",
  "timestamp": "2026-08-13T14:25:02Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "email": "jane.doe@example.com",
    "message": "I need help with my last invoice.",
    "source": "CUSTOM_FORM",
    "formCodeName": "contact_form",
    "formName": "Contact form",
    "fields": [
      {"name": "email", "value": "jane.doe@example.com", "type": "email"},
      {"name": "order_number", "value": "A-10293", "type": "text"},
      {"name": "message", "value": "I need help with my last invoice.", "type": "textarea"}
    ]
  }
}
  • email - adresa koju je posetilac ostavio i na koju vaš tim za podršku treba da odgovori.
  • source - CUSTOM_FORM kada iza kontakt obrasca stoji prilagođeni obrazac, CONTACT_FORM za ugrađeni obrazac.
  • formCodeName / formName - identifikuju prilagođeni obrazac koji stoji iza zahteva; oba su null za ugrađeni obrazac.
  • Kada se koristi prilagođeni obrazac, svako polje definisano u tom obrascu je uključeno u fields (isti format {name, value, type} kao kod lead.created). Kod ugrađenog kontakt obrasca popunjeni su samo email i message, fields je prazan niz, a identifikatori obrasca su null.
  • message - mapirano polje poruke ili sve popunjene vrednosti spojene zajedno kada u obrascu nije definisano polje za poruku.

custom_form.submitted

Aktivira se za svako slanje prilagođenog obrasca, bez obzira na svrhu obrasca. Imajte na umu da obrasci čija je svrha prikupljanje lidova ili kontakt sa čovekom takođe aktiviraju sopstveni namenski događaj lead.created / contact_form.submitted - pretplatite se na jedan ili drugi u zavisnosti od toga da li želite opšti ili specijalizovani prikaz, umesto da kreirate duplirane poslovne akcije. Ove dve porodice događaja imaju različite ID-jeve događaja; kombinacija conversationId i timestamp nije pouzdan identifikator slanja.

{
  "eventId": "6c1d8e3f-2a5b-4c7d-9e0f-1a4b6c8d0e2f",
  "eventType": "custom_form.submitted",
  "timestamp": "2026-08-13T14:27:45Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "formCodeName": "warranty_claim",
    "formName": "Warranty claim",
    "fields": [
      {"name": "order_number", "value": "A-10293", "type": "text"},
      {"name": "issue", "value": "Damaged on arrival", "type": "textarea"},
      {"name": "photo", "value": "https://api.chatlab.com/aichat/customform/download?key=...&token=...", "type": "file"}
    ],
    "purpose": "STANDALONE"
  }
}
  • formCodeName - stabilan mašinski naziv obrasca, nepromenjen kada preimenujete obrazac; koristite ga za usmeravanje poslatih podataka u vašem sistemu. formName je naziv koji se prikazuje posetiocima.
  • fields koristi iste unose formata {name, value, type} kao lead.created: multichoice vrednosti su nizovi, a vrednosti datoteka su linkovi za preuzimanje.
  • purpose - STANDALONE, LEAD_COLLECTION ili HUMAN_CONTACT, u zavisnosti od toga kako je obrazac povezan sa četbotom.

conversation.started

Aktivira se kada posetilac pošalje prvu poruku u novom razgovoru.

{
  "eventId": "8e2f0a4b-3c6d-4e8f-a1b2-2c5d7e9f1a3b",
  "eventType": "conversation.started",
  "timestamp": "2026-08-13T14:20:11Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "firstMessage": "Do you ship to Canada?",
    "chatSource": "WIDGET",
    "byAdmin": false,
    "countryCode": "PL",
    "ipAddress": "83.12.44.7"
  }
}
  • firstMessage - tačan tekst uvodne poruke posetioca. Vrednost je null ako je razgovor otvoren bez sadržaja poruke.
  • chatSource - kanal preko kojeg je razgovor pristigao: WIDGET, WHATSAPP, MESSENGER, VOICE, VOICE_PHONE, API, BOOKING, AIRBNB ili IDOBOOKING.
  • byAdmin - ima vrednost true kada razgovor potiče iz pregleda četbota u ChatLab kontrolnoj tabli, a ne od stvarnog posetioca. Koristite ovo kako biste sprečili ulazak sopstvenih test ćaskanja u CRM.
  • countryCode - ISO kod države određen na osnovu IP adrese posetioca, ili null kada ga nije bilo moguće utvrditi.
  • ipAddress - IP adresa posetioca kako je vidi ChatLab, ili null kada nije dostupna. Tretirajte je kao lični podatak u skladu sa GDPR-om i čuvajte je samo ako imate pravni osnov.

conversation.rated

Aktivira se kada posetilac pošalje pozitivnu ili negativnu ocenu razgovora (pogledajte Ocenjivanje razgovora).

{
  "eventId": "1b4c6d8e-5f0a-4b2c-8d3e-4f7a9b1c3d5e",
  "eventType": "conversation.rated",
  "timestamp": "2026-08-13T14:31:09Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "rating": "POSITIVE"
  }
}
  • rating - POSITIVE ili NEGATIVE. Poništavanje ocene ne aktivira ovaj događaj, tako da nikada ne dobijate neutralnu vrednost.

conversation.summarized

Aktivira se kada ChatLab generiše rezime završenog razgovora.

{
  "eventId": "4d7e9f1a-6b2c-4d4e-9f0a-5b8c0d2e4f6a",
  "eventType": "conversation.summarized",
  "timestamp": "2026-08-13T14:45:00Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "summary": "Visitor asked about shipping to Canada and delivery times. The bot confirmed availability and quoted 5-7 business days. Visitor left satisfied.",
    "language": "en_US"
  }
}
  • summary - tekst generisanog rezimea. Generisanje rezimea je asinhrono i zavisi od konfiguracije rezimea za bota i rasporeda obrade; nemojte pretpostavljati fiksno vreme isporuke.
  • language - interni locale bota prosleđen u proces rezimiranja, na primer en_US; nemojte pretpostavljati da se ovo poklapa sa jezikom na kojem je posetilac vodio razgovor.

client.summarized

Aktivira se kada ChatLab osveži AI profil klijenta. Profil se ponovo gradi na osnovu prethodnog profila i rezimea razgovora koji se upravo završio, i može biti generisan nakon događaja conversation.summarized. Redosled isporuke nije garantovan. Telo poruke sadrži imejl kada je poznat, ali ChatLab može čuvati i klijente bez imejla.

{
  "eventId": "b5d8f1a3-7c2e-4d9b-a6f0-1e3c5a7b9d2f",
  "eventType": "client.summarized",
  "timestamp": "2026-08-18T09:12:04Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "clientEmail": "jane.doe@example.com",
    "client": {
      "email": "jane.doe@example.com",
      "name": "Jane Doe",
      "phone": "+1 555 0123",
      "countryCode": "PL",
      "ipAddress": "83.12.44.7"
    },
    "clientSummary": "Returning customer interested in international shipping. Asked about delivery times to Canada twice and about return costs once."
  }
}
  • clientEmail - identifikator za povezivanje klijenta sa vašim CRM-om. Ima vrednost null za anonimne posetioce koji nikada nisu ostavili adresu, pri čemu se događaj i dalje aktivira za njih - preskočite te isporuke ako se vaša integracija oslanja na imejl.
  • client - kontakt zapis koji ChatLab čuva za ovu osobu: email, name, phone, countryCode i ipAddress. Svaki ključ je uvek prisutan; nepoznate vrednosti su null.
  • clientSummary - pun tekst profila kao običan tekst, a ne diff. On zamenjuje prethodni rezime bez obzira na to kakav je bio, pa ga sačuvajte prepisivanjem umesto dopisivanjem.
  • Profil se ponovo gradi samo za botove sa omogućenom funkcijom chat memory (memorija ćaskanja), i samo za razgovore koji su bili neaktivni dovoljno dugo da bi se generisao rezime - očekujte ovaj događaj nekoliko minuta nakon završetka razgovora, a ne odmah.

live_chat.requested

Aktivira se kada AI preda razgovor na Live Chat (čat uživo), bilo zato što je posetilac zatražio čoveka ili zato što je bot odlučio da je potreban ljudski operater.

{
  "eventId": "7a0b2c4d-8e3f-4a5b-b0c1-6d9e1f3a5b7c",
  "eventType": "live_chat.requested",
  "timestamp": "2026-08-13T14:33:20Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "requestedBy": "AI"
  }
}
  • requestedBy - trenutno uvek AI, jer preusmeravanje uvek pokreće akcija ćaskanja uživo samog bota, uključujući i situaciju kada posetilac to zatraži običnim rečima. Tretirajte ovo kao otvoreni enum: obradite nepoznate vrednosti umesto da strogo očekujete samo AI.
  • Ovaj događaj pokriva situaciju kada AI akcija zahteva preusmeravanje, ali ne i svaki način na koji posetilac može otvoriti ćaskanje uživo. On nije dokaz da se operater pridružio.

live_chat.started

Aktivira se kada se sesija ćaskanja uživo kreira nakon što posetilac pošalje obrazac za preusmeravanje. Ovo se dešava pre nego što se operater nužno pridruži ili odgovori; nemojte ovo tretirati kao dokaz uključenja čoveka.

{
  "eventId": "0c3d5e7f-9a4b-4c6d-a1b2-7e0f2a4b6c8d",
  "eventType": "live_chat.started",
  "timestamp": "2026-08-13T14:33:55Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {}
}
  • data je namerno prazno. Sve što vam je potrebno nalazi se u koverti: botId identifikuje četbota, a conversationId / sessionId povezuju događaj sa razgovorom. Možete ga primiti i bez prethodnog događaja live_chat.requested, na primer kada je posetilac upotrebio kontrolu za ćaskanje uživo u samom vidžetu.

live_chat.ended

Aktivira se kada se sesija ćaskanja uživo završi.

{
  "eventId": "2e5f7a9b-0c5d-4e7f-b2c3-8f1a3b5c7d9e",
  "eventType": "live_chat.ended",
  "timestamp": "2026-08-13T14:52:41Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "durationSeconds": 1126
  }
}
  • durationSeconds - proteklo trajanje sesije ćaskanja uživo u sekundama, računato od kreiranja sesije, uključujući i vreme čekanja na operatera. Polje se izostavlja u retkom slučaju kada se sesija završi a da nikada nije zapravo započeta.

ai_action.executed

Aktivira se svaki put kada bot izvrši AI action (AI akciju) - poziv upravljane integracije ili prilagođenu API funkciju. Ovo je događaj visokog obima: aktivan e-commerce bot može izvršiti stotine akcija dnevno, a jedna jedina poruka posetioca može pokrenuti nekoliko njih. Pretplatite se na njega na posebnoj krajnjoj tački ili se uverite da vaš prijemnik može da podnese toliki obim.

{
  "eventId": "5f8a0b2c-1d6e-4f8a-c3d4-9a2b4c6d8e0f",
  "eventType": "ai_action.executed",
  "timestamp": "2026-08-13T14:21:03Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "actionName": "search_products",
    "status": "SUCCESS",
    "durationMs": 842,
    "errorMessage": null
  }
}
  • actionName - naziv izvršene akcije kako je vidi AI, na primer search_products za upravljanu integraciju ili naziv koji ste dali prilagođenoj API akciji.
  • status - SUCCESS ili ERROR.
  • durationMs - koliko je akcija trajala, u milisekundama. Korisno za uočavanje spore integracije pre nego što se posetioci požale na nju.
  • errorMessage - razlog neuspeha, popunjen samo kada je status jednak ERROR; u suprotnom je null.

webhook.test

Šalje ga dugme Send sample event (Pošalji probni događaj) kada pokrenete jednostavnu proveru povezivanja. Potpisan je potpuno isto kao stvarni događaj.

{
  "eventId": "9b2c4d6e-3f8a-4b0c-d5e6-0b3c5d7e9f1a",
  "eventType": "webhook.test",
  "timestamp": "2026-08-13T14:10:00Z",
  "botId": 1234,
  "botName": "Support Bot",
  "conversationId": "conv_a1b2c3",
  "sessionId": "sess_x9y8z7",
  "data": {
    "message": "Test delivery from ChatLab"
  }
}
  • message - fiksni tekst, uvek isti. Polja u koverti nose probne vrednosti, pa nikada nemojte tretirati isporuku webhook.test kao stvarne podatke.
  • Ovo je jedini tip događaja na koji se ne možete pretplatiti na krajnjoj tački: šalje se na zahtev iz administratorskog panela i uvek stiže na krajnju tačku na koju ste kliknuli, bez obzira na to koje događaje ona sluša.

Bezbednost: verifikacija isporuka

Svaka isporuka nosi četiri zaglavlja:

Zaglavlje Vrednost
X-ChatLab-Signature sha256=<hex hmac> - HMAC-SHA256 potpis tela zahteva
X-ChatLab-Timestamp Unix vreme u sekundama kada je isporuka potpisana
X-ChatLab-Event Tip događaja, npr. lead.created
X-ChatLab-Delivery Jedinstveni ID isporuke, jednak vrednosti eventId iz tela zahteva

Potpis se računa kao HMAC-SHA256 nad stringom {timestamp}.{rawBody} pomoću tajnog ključa vaše krajnje tačke, gde je {timestamp} vrednost zaglavlja X-ChatLab-Timestamp, a {rawBody} sirovo, neobrađeno telo zahteva. Uvek verifikujte na osnovu sirovih bajtova - ponovna serijalizacija raščlanjenog JSON-a promeniće redosled bajtova i poništiti potpis.

Da biste se zaštitili od napada ponavljanjem (replay attacks), odbijte isporuke čiji je X-ChatLab-Timestamp stariji od 5 minuta.

Node.js

const crypto = require('crypto');

function verifyChatLabSignature(req, secret) {
    const signature = req.headers['x-chatlab-signature'];
    const timestamp = req.headers['x-chatlab-timestamp'];
    if (typeof signature !== 'string' || typeof timestamp !== 'string') return false;
    if (!/^\d+$/.test(timestamp) || !Number.isSafeInteger(Number(timestamp))) return false;
    if (!Buffer.isBuffer(req.rawBody)) return false;

    // Odbijte zastarele isporuke (starije od 5 minuta)
    const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
    if (ageSeconds > 300) return false;

    // rawBody moraju biti sirovi bajtovi tela zahteva, a ne ponovo serijalizovan JSON.
    // Sa Express-om: app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }))
    const expected = 'sha256=' + crypto
        .createHmac('sha256', secret)
        .update(timestamp + '.')
        .update(req.rawBody)
        .digest('hex');

    const a = Buffer.from(signature);
    const b = Buffer.from(expected);
    return a.length === b.length && crypto.timingSafeEqual(a, b);
}

PHP

<?php
function verifyChatLabSignature(string $secret): bool
{
    $signature = $_SERVER['HTTP_X_CHATLAB_SIGNATURE'] ?? '';
    $timestamp = $_SERVER['HTTP_X_CHATLAB_TIMESTAMP'] ?? '';
    if ($signature === '' || $timestamp === '' || !ctype_digit($timestamp)) {
        return false;
    }

    // Odbijte zastarele isporuke (starije od 5 minuta)
    if (abs(time() - (int) $timestamp) > 300) {
        return false;
    }

    $rawBody = file_get_contents('php://input');
    $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);

    return hash_equals($expected, $signature);
}

Ako verifikacija ne uspe, odgovorite statusom 401 i odbacite podatke. Nikada nemojte obrađivati neverifikovane isporuke - svako ko otkrije vaš URL može mu poslati proizvoljan JSON putem POST zahteva.

Ponašanje isporuke

Upoznajte se sa ovim pravilima isporuke pre nego što počnete sa radom na webhook-ovima:

  • Odgovorite brzo. Vaša krajnja tačka (endpoint) mora da odgovori u roku od 3 sekunde ili se isporuka smatra neuspešnom. Verifikujte potpis, trajno smestite događaj u red čekanja i potvrdite prijem statusom 2xx unutar tog vremenskog okvira. Sporije CRM pozive i poslovnu obradu obavljajte asinhrono.
  • Pošalji i zaboravi (fire-and-forget), najviše jednom (at-most-once). Odgovarajuće omogućene krajnje tačke dobijaju najviše jedan pokušaj isporuke - nema ponovnih pokušaja. Pauzirane krajnje tačke i aktivirani mehanizmi prekidača (open circuits) mogu sprečiti čak i taj jedan pokušaj. Ako je vaša krajnja tačka nedostupna, istekne joj vreme za odgovor ili vrati status koji nije 2xx, taj događaj je izgubljen i neće biti ponovo isporučen. Webhook-ovi su obaveštenja, a ne skladište replikovanih podataka: koristite krajnje tačke za razgovore u okviru Bot Talk API-ja ili izvoz lead-ova kako biste usaglasili sačuvane podatke gde je to podržano. Management API pokriva podešavanja bota i podatke o korišćenju, a ne kompletnu arhivu događaja. Neki događaji se ne mogu rekonstruisati putem ovih interfejsa.
  • Prekidač kola (circuit breaker). Nakon 5 uzastopnih neuspešnih isporuka za par krajnja tačka/bot, isporuke za taj par se pauziraju na 5 minuta. Događaji koji se dese tokom pauze se odbacuju, a evidencija isporuke prikazuje unose CIRCUIT_OPEN kako bi se identifikovali sprečeni pokušaji. Isporuke preskočene zbog otvorenog prekidača ne računaju se u automatsko onemogućavanje.
  • Automatsko onemogućavanje. Provera se vrši u trenutku kada isporuka ne uspe, nikada na osnovu tajmera. Ako isporuka ne uspe, a 7 dana nije bilo nijedne uspešne isporuke - računajući od poslednjeg uspeha ili od datuma kreiranja krajnje tačke ako nikada nije uspela - krajnja tačka se isključuje i dobijate obaveštenje putem e-pošte. Jedan jedini status 2xx u bilo kom trenutku resetuje taj tajmer. Krajnja tačka koja ne prima saobraćaj se nikada ne onemogućava, jer ništa ne doživljava neuspeh. Ponovo je omogućite u Account settings (Podešavanja naloga) čim popravite svoj prijemnik; brojač neuspeha i oznaka automatskog onemogućavanja se brišu kada je ponovo uključite, a propušteni događaji dok je bila isključena se ne nadoknađuju.
  • 410 Gone. Ako vaša krajnja tačka odgovori sa HTTP 410 Gone, ChatLab je odmah onemogućava. Iskoristite ovo da programski ugasite krajnju tačku sa strane prijemnika.
  • Idempotentnost. Dvostruke isporuke se ne očekuju u normalnom radu, ali ako vaša obrada mora biti striktno idempotentna, izvršite deduplikaciju prema eventId (takođe dostupnom u zaglavlju X-ChatLab-Delivery).

Redosled isporuke nije garantovan. Sačuvajte ID događaja i učinite poslovnu obradu idempotentnom. Zadržite sopstvenu retenciju i kontrole pristupa za sadržaje webhook-ova, koji mogu sadržati lične podatke i linkove za preuzimanje datoteka.

Primeri potpisa koriste Node.js crypto i PHP funkciju hash_equals. Preuzmite sirovo telo zahteva pre nego što ga parsirate.

Ograničenja

  • Do 10 webhook krajnjih tačaka po nalogu.
  • Zadržavanje evidencije isporuke: 14 dana. Stariji unosi se automatski brišu.

Povezani članci