Pagalbos centras
Chat API

Webhooks

Paskutinį kartą atnaujinta:

Webhook apžvalga

Webhook leidžia ChatLab pranešti Jūsų sistemoms tą pačią akimirką, kai kas nors įvyksta Jūsų pokalbių robotuose. Užuot siuntus užklausas į Management API ar eksportavus duomenis rankiniu būdu, Jūs užregistruojate HTTPS galinį tašką (endpoint), o ChatLab siunčia į jį pasirašytą HTTP POST užklausą realiuoju laiku - kai lankytojas palieka kontaktus (lead), pateikia kontaktinę formą, įvertina pokalbį, paprašo žmogaus pagalbos arba kai įvykdomas AI veiksmas.

Tipiniai panaudojimo atvejai:

  • persiųsti naujus kontaktus tiesiai į savo CRM tą pačią sekundę, kai jie užfiksuojami
  • pranešti komandai Slack platformoje, kai lankytojas paprašo tiesioginio pokalbio (live chat)
  • perduoti pokalbių įvertinimus ir santraukas į savo analitikos sistemas
  • stebėti AI veiksmų (AI actions) vykdymą ir gauti įspėjimus apie klaidas

Prieinamumas: webhook funkcijos pasiekiamos nuo Standard plano (funkcija: Webhooks).

Kur konfigūruoti: administratoriaus programėlėje atidarykite Account settings -> Webhooks (Paskyros nustatymai -> Webhook; šalia Management API skilties). Webhook veikia paskyros lygiu - vienas galinis taškas gali gauti įvykius iš visų Jūsų robotų arba iš filtruoto jų poaibio.

Galinio taško nustatymas

  1. Atidarykite Account settings -> Webhooks ir spustelėkite Create endpoint (Sukurti galinį tašką).
  2. Užpildykite galinio taško formą:
    • Name (Pavadinimas) - etiketė Jūsų pačių patogumui, pvz., „CRM sinchronizavimas“ arba „Slack pranešimai“.
    • URL - HTTPS adresas, į kurį ChatLab siųs POST įvykius.
    • Events (Įvykiai) - pasirinkite, kuriuos įvykių tipus šis galinis taškas turi gauti (žr. katalogą žemiau). Pasirinkite tik tai, ko reikia; didelio srauto įvykiai, tokie kaip ai_action.executed, gali generuoti daug užklausų.
    • Bot filter (Roboto filtras) (neprivaloma) - apribokite galinį tašką konkrečiais robotais. Palikite tuščią, jei norite gauti įvykius iš visų Jūsų paskyros robotų.
    • Custom form filter (Tinkintos formos filtras) (neprivaloma) - nukreipia vienos tinkintos formos pateikimus į šį galinį tašką. Tai apriboja tik custom_form.submitted įvykį; visi kiti prenumeruojami įvykiai (kontaktai, susisiekimo užklausos, pokalbiai, tiesioginis pokalbis, AI veiksmai) bus pristatomi nepriklausomai nuo šio nustatymo.
  3. Pateikite formą. Galinio taško slaptasis raktas (secret) parodomas tik vieną kartą sėkmės lange - nukopijuokite jį dabar ir saugiai išsaugokite. Jo prireiks parašams tikrinti (žr. skiltį „Sauga“ žemiau). Vėliau grynojo teksto peržiūrėti nebebus įmanoma.

Kiekvienas galinis taškas taip pat turi:

  • Enable/disable toggle (Įjungimo / išjungimo perjungiklis) - pristatymo pristabdymas neištrinant galinio taško. Išjungti galiniai taškai įvykius tiesiog ignoruoja (jie nėra kaupiami eilėje vėlesniam laikui).
  • Send sample event (Siųsti pavyzdinį įvykį) - nusiunčia pasirašytą bandomąją užklausą į Jūsų URL, kad galėtumėte patikrinti visą priėmimo grandinę. Galite pasirinkti įvykio tipą ir prieš siųsdami redaguoti pavyzdines reikšmes, kad gaviklis matytų tikroviškus duomenis. Testas pristatomas kaip įprastas įvykis su Jūsų pasirinktu eventType (arba kaip webhook.test, jei atliekamas paprastas ryšio patikrinimas).
  • Roll secret (Pakeisti slaptąjį raktą) - sugeneruoja naują slaptąjį raktą ir panaikina senojo galiojimą. Naudokite tai, jei kilo įtarimas, kad raktas galėjo nutekėti. Naujas raktas vėl parodomas tik vieną kartą. Prieš keisdami atnaujinkite gaviklio nustatymus, kitaip užklausos neatlaikys parašo patikros Jūsų pusėje.
  • Delivery log (Pristatymo žurnalas) - konkretaus galinio taško naujausių pristatymų sąrašas su laiko žyma, įvykio tipu, Jūsų serverio grąžintu HTTP statusu ir atsakymo laiku. Čia matomi nesėkmingi pristatymai ir grandinės pertraukiklio (circuit breaker) pauzės. Žurnalas saugomas 14 dienų.

Įvykio apvalkalas (envelope)

Kiekvienas pristatymas yra HTTP POST užklausa su Content-Type: application/json. Turinys visada turi tą patį apvalkalą; objektas data priklauso nuo konkretaus įvykio tipo:

{
  "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 - unikalus kiekvienam įvykiui. Naudokite jį dublikatams šalinti (deduplikacijai), jei Jūsų apdorojimas turi būti idempotentiškas.
  • eventType - vienas iš žemiau aprašytų tipų; taip pat siunčiamas X-ChatLab-Event antraštėje.
  • timestamp - ISO 8601 UTC laikas, kada įvyko įvykis.
  • botId / botName - robotas, kuriam priklauso įvykis.
  • conversationId / sessionId - pokalbio kontekstas, kai taikoma.

Įvykių katalogas

lead.created

Suveikia, kai lankytojas pateikia savo kontaktinius duomenis - per kontaktų surinkimo formą, tiesioginio pokalbio pradinę formą arba tinkintą formą, naudojamą kontaktams rinkti.

{
  "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 - būdas, kaip buvo užfiksuoti kontaktai: LEAD_COLLECTION_FORM (kontaktų surinkimo forma), LIVE_CHAT_FORM (tiesioginio pokalbio pradinė forma), CONVERSATION (AI suprato ir išsaugojo duomenis pokalbio metu), ADMIN_DATA_UPDATE arba UPDATE_CLIENT_CONTEXT (redaguota ChatLab pusėje). Pagalbos kreipimosi į žmogų formos pateikimai niekada nesuaktyvina šio įvykio - vietoj to jie aktyvina contact_form.submitted.
  • email, name, phone - kontaktiniai duomenys, priskirti šiam kontaktų įrašui.
  • Kai kontaktų rinkimui naudojama tinkinta forma, kiekvienas joje nurodytas laukas įtraukiamas į fields formos nustatyta tvarka, o formCodeName / formName identifikuoja formą. Naudojant klasikinę formą, abu yra null, o fields yra tuščias masyvas.
  • Kiekvienas įrašas fields masyve yra {name, value, type}. name yra techninis lauko pavadinimas, nekintantis redaguojant etiketę - naudokite jį susiejimui su savo CRM.
  • Laukuose su multichoice reikšmė value yra pasirinktų parinkčių masyvas. Žymimųjų langelių (checkbox) laukai yra atskiri įrašai su reikšmėmis "true" / "false".
  • Laukuose su file reikšmė value yra įkelto failo atsisiuntimo nuoroda; webhook pranešime failo turinys niekada nesiunčiamas.
  • pageUrl - puslapis, kuriame lankytojas buvo pateikimo metu.

contact_form.submitted

Suveikia, kai lankytojas pateikia žmogaus pagalbos kontaktinę formą arba tinkintą formą, naudojamą susisiekti su žmogumi.

{
  "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 - lankytojo paliktas adresas, kuriuo Jūsų palaikymo komanda turėtų atsakyti.
  • source - CUSTOM_FORM, kai kontaktinei formai naudojama tinkinta forma, arba CONTACT_FORM, kai naudojama integruota forma.
  • formCodeName / formName - nurodo užklausai naudotą tinkintą formą; integruotos formos atveju abu yra null.
  • Kai naudojama tinkinta forma, visi joje nurodyti laukai įtraukiami į fields (tokiu pačiu {name, value, type} formatu kaip ir lead.created). Su integruota kontaktine forma užpildomi tik email bei message, fields yra tuščias masyvas, o formos identifikatoriai yra null.
  • message - priskirtas pranešimo laukas arba visų užpildytų laukų reikšmės, sujungtos kartu, jei formoje nėra atskiro pranešimo lauko.

custom_form.submitted

Suveikia pateikus bet kurią tinkintą formą, nepriklausomai nuo jos paskirties. Atkreipkite dėmesį, kad formos, kurių paskirtis yra kontaktų rinkimas arba susisiekimas su žmogumi, taip pat sukelia joms skirtus įvykius lead.created / contact_form.submitted - pasirinkite vieną ar kitą priklausomai nuo to, ar norite bendro, ar specializuoto vaizdo, ir šalinkite dublikatus pagal conversationId + timestamp, jei prenumeruojate abu.

{
  "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 - nekintantis sistemos formos pavadinimas, kuris nesikeičia pervardijus formą; naudokite jį duomenų nukreipimui savo sistemoje. formName yra lankytojams rodomas pavadinimas.
  • fields naudoja tuos pačius {name, value, type} įrašus kaip lead.created: kelių pasirinkimų reikšmės yra masyvai, failų reikšmės yra atsisiuntimo nuorodos.
  • purpose - STANDALONE, LEAD_COLLECTION arba HUMAN_CONTACT, priklausomai nuo to, kaip forma susieta su pokalbių robotu.

conversation.started

Suveikia, kai lankytojas išsiunčia pirmąją naujo pokalbio žinutę.

{
  "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 - tikslus lankytojo pradinės žinutės tekstas. null, jei pokalbis buvo pradėtas be žinutės turinio.
  • chatSource - kanalas, per kurį gautas pokalbis: WIDGET, WHATSAPP, MESSENGER, VOICE, VOICE_PHONE, API, BOOKING, AIRBNB arba IDOBOOKING.
  • byAdmin - true, kai pokalbis vyksta iš pokalbių roboto peržiūros ChatLab administratoriaus skydelyje, o ne su tikru lankytoju. Naudokite tai, kad bandomieji pokalbiai nepatektų į Jūsų CRM.
  • countryCode - ISO šalies kodas, nustatytas pagal lankytojo IP adresą; null, jei nustatyti nepavyko.
  • ipAddress - lankytojo IP adresas, kurį mato ChatLab; null, jei neprieinamas. Pagal BDAR traktuokite jį kaip asmens duomenis ir saugokite tik turėdami teisinį pagrindą.

conversation.rated

Suveikia, kai lankytojas įvertina roboto atsakymą teigiamai arba neigiamai (žr. Pokalbių vertinimas).

{
  "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 arba NEGATIVE. Įvertinimo atšaukimas šio įvykio nesuaktyvina, todėl neutralios reikšmės niekada negausite.

conversation.summarized

Suveikia, kai ChatLab sugeneruoja baigto pokalbio santrauką.

{
  "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"
  }
}
  • summary - sugeneruotas santraukos tekstas. Santraukos sukuriamos praėjus kelioms minutėms po to, kai pokalbis tampa neaktyvus, todėl šis įvykis atkeliauja vėliau nei kiti pokalbio įvykiai.
  • language - ISO kalbos kodas, kuria parašyta santrauka (atitinka pokalbio kalbą).

client.summarized

Suveikia, kai ChatLab atnaujina kliento AI profilį. Profilis atkuriamas iš ankstesnio profilio ir ką tik pasibaigusio pokalbio santraukos, todėl šis įvykis eina po conversation.summarized tam pačiam pokalbiui. Klientai identifikuojami pagal el. paštą, todėl šis adresas pakartojamas viršutiniame data lygyje.

{
  "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 - identifikatorius, skirtas susieti klientą su Jūsų CRM. Jis yra null anoniminiams lankytojams, kurie niekada nepaliko adreso; šiems lankytojams įvykis vis tiek suveikia - praleiskite šiuos pristatymus, jei Jūsų integracija paremta el. paštu.
  • client - kontaktinė informacija, kurią ChatLab turi apie šį asmenį: email, name, phone, countryCode ir ipAddress. Visi raktai pateikiami visada; nežinomos reikšmės yra null.
  • clientSummary - visas profilio tekstas kaip paprastas tekstas, o ne skirtumai (diff). Jis pakeičia bet kokią buvusią santrauką, todėl išsaugokite jį perrašydami, o ne pridėdami gale.
  • Profilis atkuriamas tik tiems robotams, kuriuose įjungta pokalbių atmintis (chat memory), ir tik tiems pokalbiams, kurie buvo neaktyvūs pakankamai ilgai, kad būtų apibendrinti - šio įvykio tikėkitės praėjus kelioms minutėms po pokalbio pabaigos, o ne iš karto.

live_chat.requested

Suveikia, kai AI perduoda pokalbį į tiesioginį pokalbį, nes lankytojas paprašė žmogaus pagalbos arba robotas nusprendė, kad žmogus yra būtinas.

{
  "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 - šiuo metu visada AI, nes perdavimą visada inicijuoja roboto tiesioginio pokalbio veiksmas, įskaitant atvejus, kai lankytojas to paprašo žodžiais. Traktuokite tai kaip atvirą reikšmių sąrašą (open enum): apdorokite nežinomas reikšmes užuot griežtai reikalavę tik AI.
  • Įvykis reiškia, kad buvo paprašyta perdavimo, o ne tai, kad operatorius jau prisijungė. Norėdami tai sužinoti, laukite live_chat.started.

live_chat.started

Suveikia, kai operatorius prisijungia ir realiai prasideda tiesioginio pokalbio sesija.

{
  "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 sąmoningai yra tuščias. Viskas, ko reikia, yra apvalkale: botId identifikuoja pokalbių robotą, o conversationId / sessionId susieja įvykį su pokalbiu, apie kurį jau gavote live_chat.requested.

live_chat.ended

Suveikia, kai tiesioginio pokalbio sesija baigiasi.

{
  "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 - kiek laiko operatorius dalyvavo pokalbyje, skaičiuojant nuo sesijos pradžios. Šis laukas praleidžiamas retais atvejais, kai sesija baigiasi taip ir neprasidėjusi.

ai_action.executed

Suveikia kiekvieną kartą, kai robotas įvykdo AI veiksmą - valdomą integracijos iškvietimą arba pasirinktinę API funkciją. Tai didelio srauto įvykis: aktyvus e. prekybos robotas gali atlikti šimtus veiksmų per dieną, o viena lankytojo užklausa gali sukelti kelis veiksmus iš eilės. Užsiprenumeruokite jį atskirame galiniame taške arba įsitikinkite, kad Jūsų gaviklis pajėgus apdoroti tokį srautą.

{
  "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 - atlikto veiksmo pavadinimas, kaip jį mato AI, pavyzdžiui, search_products valdomai integracijai arba Jūsų suteiktas pavadinimas pasirinktiniam API veiksmui.
  • status - SUCCESS arba ERROR.
  • durationMs - veiksmo vykdymo trukmė milisekundėmis. Naudinga norint pastebėti lėtą integraciją dar prieš lankytojams pradedant skųstis.
  • errorMessage - klaidos priežastis; pateikiama tik tada, kai status yra ERROR; priešingu atveju reikšmė yra null.

webhook.test

Siunčiamas mygtuku Send sample event (Siųsti pavyzdinį įvykį), kai atliekate paprastą ryšio patikrinimą. Pasirašomas lygiai taip pat, kaip ir tikras įvykis.

{
  "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 - fiksuotas tekstas, visada toks pat. Apvalkalo laukuose pateikiamos pavyzdinės reikšmės, todėl niekada nelaikykite webhook.test siuntimo tikrais duomenimis.
  • Tai vienintelis įvykio tipas, kurio negalite užsiprenumeruoti galiniame taške: jis siunčiamas pagal pareikalavimą iš administratoriaus skydelio ir visada pasiekia tą galinį tašką, kurį spustelėjote, nepriklausomai nuo to, kokių įvykių jis klausosi.

Sauga: pristatymų patvirtinimas

Kiekvienas pristatymas turi keturias antraštes:

Header Reikšmė
X-ChatLab-Signature sha256=<hex hmac> - naudingosios apkrovos (payload) HMAC-SHA256 parašas
X-ChatLab-Timestamp „Unix“ laikas sekundėmis, kada pristatymas buvo pasirašytas
X-ChatLab-Event Įvykio tipas, pvz., lead.created
X-ChatLab-Delivery Unikalus pristatymo ID, sutampantis su turinio eventId

Parašas apskaičiuojamas kaip HMAC-SHA256 eilutei {timestamp}.{rawBody}, naudojant jūsų galutinio taško slaptąjį raktą (secret), kur {timestamp} yra X-ChatLab-Timestamp reikšmė, o {rawBody} yra neapdorotas, neanalizuotas užklausos turinys. Visada tikrinkite pagal neapdorotus (raw) baitus - išanalizuoto JSON pakartotinis serializavimas pakeis baitų seką ir pažeis parašą.

Norėdami apsisaugoti nuo pakartotinio siuntimo atakų (replay attacks), atmeskite pristatymus, kurių X-ChatLab-Timestamp yra senesnis nei 5 minutės.

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 (!signature || !timestamp) return false;

    // Reject stale deliveries (older than 5 minutes)
    const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
    if (ageSeconds > 300) return false;

    // rawBody must be the raw request body bytes, not re-serialized JSON.
    // With Express: app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }))
    const expected = 'sha256=' + crypto
        .createHmac('sha256', secret)
        .update(timestamp + '.' + 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 === '') {
        return false;
    }

    // Reject stale deliveries (older than 5 minutes)
    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);
}

Jei patvirtinimas nepavyksta, atsakykite su 401 ir atmeskite naudingąją apkrovą. Niekada neapdorokite nepatvirtintų pristatymų - bet kas, sužinojęs jūsų URL, gali išsiųsti į jį bet kokį JSON.

Pristatymo elgsena

Prieš kurdami sprendimus su „webhook“, susipažinkite su šiomis garantijomis:

  • Atsakykite greitai. Jūsų galutinis taškas turi atsakyti per 3 sekundes, kitaip pristatymas bus laikomas nesėkmingu. Nedelsdami atsakykite 2xx kodu ir apdorokite naudingąją apkrovą asinchroniškai (įdėkite į eilę ir tik tada patvirtinkite) - prieš atsakydami nevykdykite CRM užklausų ar įrašymo į duomenų bazę operacijų.
  • Išsiųskite ir pamirškite, daugiausiai vieną kartą (at-most-once). Kiekvienas įvykis gauna tiksliai vieną pristatymo bandymą - pakartotinių bandymų nėra. Jei jūsų galutinis taškas neveikia, baigiasi laukimo laikas arba grąžinamas ne 2xx būsenos kodas, tas įvykis prarandamas ir nebebus pristatytas iš naujo. „Webhook“ yra pranešimai, o ne replikuojama duomenų saugykla: kai reikia garantuoto išsamumo, sutikrinkite duomenis naudodami Management API arba savo kontaktų (leads) eksportus.
  • Grandinės pertraukiklis (circuit breaker). Po 5 iš eilės nesėkmingų pristatymų vienam botui, to boto pristatymai pristabdomi 5 minutėms. Per šią pauzę įvykę įvykiai atmetami, o pristatymo žurnale rodomi CIRCUIT_OPEN įrašai, kad matytumėte, kada tiksliai ir kodėl srautas buvo sustabdytas. Pristatymai, praleisti dėl atidarytos grandinės, nėra įskaičiuojami į automatinį išjungimą.
  • Automatinis išjungimas. Tikrinimas vykdomas pristatymo nesėkmės momentu, niekada ne pagal laikmatį. Jei pristatymas nepavyksta ir 7 dienas nebuvo jokio sėkmingo pristatymo - skaičiuojant nuo paskutinio sėkmingo atvejo arba nuo galutinio taško sukūrimo datos, jei sėkmingų pristatymų dar nebuvo, - galutinis taškas išjungiamas, o Jūs gaunate pranešimą el. paštu. Bet kuriuo metu gautas bent vienas 2xx atsakymas iš naujo nustato šį laiką. Galutinis taškas, kuris negauna jokio srauto, niekada neišjungiamas, nes nėra jokių klaidų. Sutvarkę savo gavėją, iš naujo įjunkite jį paskyros nustatymuose (Account settings); iš naujo įjungus, klaidų skaitiklis ir automatinio išjungimo žyma išvalomi, o įvykiai, praleisti kol taškas buvo išjungtas, neatkuriami.
  • 410 Gone. Jei jūsų galutinis taškas atsako HTTP kodu 410 Gone, „ChatLab“ jį iškart išjungia. Naudokite tai, norėdami programiškai nutraukti galutinio taško veikimą iš gavėjo pusės.
  • Idempotentiškumas. Įprastai veikiant sistemai pasikartojančių pristatymų nebūna, tačiau jei jūsų apdorojimas turi būti griežtai idempotentinis, pašalinkite dublikatus pagal eventId (taip pat prieinamą X-ChatLab-Delivery antraštėje).

Apribojimai

  • Iki 10 „webhook“ galutinių taškų vienai paskyrai.
  • Pristatymo žurnalo saugojimas: 14 dienų. Senesni įrašai pašalinami automatiškai.

Susiję straipsniai