Webhooky
Webhooky posielajú každý skončený rozhovor na váš server ako JSON spolu s AI zhrnutím a údajmi o respondentovi. Pomocou nich môžete aktualizovať CRM, zakladať tikety alebo nahrávať výsledky do vlastnej databázy bez ručného exportu.
Čo sa odosiela a kedy#
Diaform posiela jednu udalosť, conversation.completed. Spustí sa, keď AI moderátor ukončí rozhovor a zhrnutie je hotové, zvyčajne chvíľu po poslednej správe.
- Pre celú organizáciu. Každý endpoint dostáva udalosti zo všetkých projektov vo vašej organizácii. Projekty rozlíšite podľa
agent_idv payloade. - Aj zrušené rozhovory spustia udalosť. Ak respondent požiada o skončenie, rozhovor skončí ako zrušený a aj tak dostanete
conversation.completeds"status": "cancelled". Než budete odpoveď považovať za dokončenú, skontrolujtestatus. - Opustené rozhovory udalosť nespustia. Ak respondent zavrie stránku bez toho, aby sa rozhovor skončil, nič sa neodošle.
- Náhľady v dashboarde udalosť nespustia. Na otestovanie dokončite rozhovor cez skutočný odkaz projektu.
Plány a oprávnenia#
Webhooky sú dostupné vo všetkých platených plánoch, nie počas skúšobnej verzie. Každý plán má limit endpointov:
| Plán | Endpointy |
|---|---|
| Starter | 3 |
| Pro | 5 |
| Business | 10 |
| Enterprise | 25 |
Endpointy môžu pridávať, pozastavovať a odstraňovať len vlastníci a administrátori. Pozrite si Členovia a roly a Správa predplatného.
Pridanie endpointu#
Otvorte webhooky
V bočnom paneli prejdite na Nastavenia → Webhooky.
Zadajte URL
Kliknite na Pridať endpoint, vložte URL do dialógu Nový webhookový endpoint a kliknite na Pridať endpoint. URL musí začínať na https://.
Uložte tajný kľúč
Dialóg Tajný kľúč webhooku ukáže váš podpisový tajný kľúč. Kliknite na Kopírovať, uložte ho do prostredia servera a potom kliknite na Hotovo.
Každý endpoint má vlastný tajný kľúč v tvare whsec_ a za ním 43 znakov. Neskôr ho môžete znova zobraziť alebo skopírovať v riadku Podpisový tajný kľúč pri danom endpointe.
Pozastavenie alebo odstránenie endpointu#
- Pozastavenie: prepnite prepínač v riadku endpointu z Aktívny na Pozastavený. Udalosti, ktoré nastanú počas pozastavenia, sa neskôr neodošlú.
- Odstránenie: kliknite na ikonu koša a potvrďte tlačidlom Odstrániť endpoint.
URL endpointu nemôžete upraviť. Ak chcete prejsť na novú URL, pridajte nový endpoint, aktualizujte server jeho tajným kľúčom a potom odstráňte starý.
Doručovanie#
| Správanie | Podrobnosti |
|---|---|
| Metóda | POST, telo vo formáte JSON |
| Rozoslanie | Odosiela sa na všetky aktívne endpointy naraz |
| Časový limit | 5 sekúnd. Dovtedy odpovedzte ľubovoľným stavom 2xx. |
| Opakovanie | Žiadne. Pri vypršaní času alebo odpovedi inej než 2xx sa udalosť znova neodošle. |
| Záznam doručení | V dashboarde nie je k dispozícii |
| Načasovanie | Ak spracovanie dokončeného rozhovoru zlyhá, Diaform ho skúša spracovať znova každých 30 minút, celkovo najviac 4 pokusy. Webhook potom môže prísť dlho po skončení rozhovoru. |
Najprv odpovedzte a pomalú prácu urobte až potom, napríklad tak, že udalosť zaradíte do fronty.
Zadajte do Diaform konečnú URL. Presmerovania sa sledujú, ale 301 alebo 302 zmení POST na GET a telo sa stratí.
Požiadavka#
POST /webhooks/diaform HTTP/1.1
Content-Type: application/json
X-Webhook-Event: conversation.completed
X-Webhook-Signature: 4fa4d553f8abf7a199e5d51ee2401320af843de438e63a2ed6bc33f4f943d906| Hlavička | Hodnota |
|---|---|
Content-Type | application/json |
X-Webhook-Event | conversation.completed |
X-Webhook-Signature | Hexadecimálny HMAC-SHA256 nespracovaného tela, podpísaný tajným kľúčom endpointu |
Hlavička s časovou pečiatkou ani ID doručenia sa neposiela. Ako kľúč idempotencie použite conversation_id: ukladajte ID, ktoré ste už spracovali, a opakovania preskočte.
Payload#
{
"conversation_id": "0f8d43a2-6df9-49c8-bf5d-fd2b6ec2d9ee",
"agent_id": "b8acb3ab-1f3d-4a7e-a846-b7f493f86b6d",
"organization_id": "4d6ecdb4-8543-4adf-ae65-1f24106d5474",
"status": "completed",
"respondent": {
"email": "alex@acme.com",
"metadata": {},
"identities": [
{ "type": "email", "value": "alex@acme.com", "source": "owner" },
{ "type": "identifier", "value": "customer-123", "source": "owner" },
{ "type": "name", "value": "Alex Morgan", "source": "ai" }
],
"invite_id": "3a4f49c7-b8f4-4c44-8f74-fd8e4277c3e4"
},
"summary": {
"respondent_email": null,
"respondent_identities": [
{ "type": "email", "value": "alex@acme.com" },
{ "type": "identifier", "value": "customer-123" },
{ "type": "name", "value": "Alex Morgan" }
],
"answers": [
{
"question_id": "V1StGXR8_Z5jdHi6B-myT",
"question_text": "How did your first week with the product go?",
"summary": "Setup was quick, but Alex struggled to invite teammates.",
"sentiment": "mixed",
"confidence": "high",
"quotes": ["I had everything running in ten minutes."]
}
],
"overall_sentiment": "mixed",
"urgent_issues": ["Teammate invites fail for Google Workspace accounts."],
"additional_insights": "Alex would pay more for an API."
},
"completed_at": "2026-05-07T12:15:41.123Z"
}Polia najvyššej úrovne#
| Pole | Typ | Popis |
|---|---|---|
conversation_id | string | ID rozhovoru. Použite ho na odstránenie duplicít. |
agent_id | string | ID projektu, ku ktorému rozhovor patrí |
organization_id | string | ID vašej organizácie |
status | string | completed alebo cancelled |
respondent | object | Kto je respondent, pozri nižšie |
summary | object | AI zhrnutie, pozri nižšie |
completed_at | string | Čas skončenia rozhovoru vo formáte ISO 8601 |
respondent#
| Pole | Typ | Popis |
|---|---|---|
email | string alebo null | E-mail respondenta, ak je známy |
metadata | object | Pri priamych odkazoch prázdne. Pri vložených rozhovoroch obsahuje údaje o vložení, napríklad "embed": true a embed_parent_origin. |
identities | array | Všetko, čo je o respondentovi známe, ako type, value a source |
identities[].type | string | Napríklad email, name, identifier, user_id, account_id alebo company |
identities[].source | string | owner: dodali ste ho vy, cez odkaz na pozvanie alebo identifikátor vo vložení. ai: získané zo správ respondenta. system: pridal Diaform. |
invite_id | string alebo null | ID pozvánky, ak rozhovor vznikol z odkazu na pozvanie. Pozrite API pre pozvánky respondentov. |
summary#
| Pole | Typ | Popis |
|---|---|---|
respondent_email | null | Zastarané pole, vždy null. E-maily čítajte z identities. |
respondent_identities | array | Identity respondenta ako type a value, bez source |
answers | array | Jedna položka pre každú otázku |
answers[].question_id | string | ID otázky vo vašom projekte |
answers[].question_text | string | Text otázky |
answers[].summary | string | Krátke zhrnutie odpovede |
answers[].sentiment | string | positive, negative, neutral alebo mixed |
answers[].confidence | string | Ako úplne respondent na otázku odpovedal: high, medium alebo low |
answers[].quotes | array alebo null | Zaujímavé citáty respondenta. null, ak žiadne nie sú. |
overall_sentiment | string | positive, negative, neutral alebo mixed |
urgent_issues | array, null alebo chýba | Kritické problémy, ktoré AI označila |
additional_insights | string, null alebo chýba | Spätná väzba, ktorá sa nehodí ku konkrétnej otázke |
Prepis ani názov projektu sa neposielajú. Ako Diaform získava identity, sa dozviete v časti Identifikácia respondentov.
Overenie podpisu#
Každú požiadavku si overte skôr, než jej budete dôverovať. Diaform podpisuje presné telo požiadavky pomocou HMAC-SHA256, ako kľúč používa tajný kľúč endpointu a hexadecimálny výsledok posiela v hlavičke X-Webhook-Signature.
- Načítajte nespracované telo ako bajty, ešte pred akýmkoľvek parsovaním JSON.
- Vypočítajte HMAC-SHA256 týchto bajtov s vaším tajným kľúčom, v hexadecimálnom tvare.
- Ak hlavička chýba alebo má inú dĺžku, požiadavku odmietnite.
- Porovnajte obe hodnoty funkciou s konštantným časom.
Použite celý tajný kľúč vrátane predpony whsec_ ako obyčajný reťazec UTF-8. Nedekódujte ho z base64.
Node.js (Express)#
const crypto = require("node:crypto");
const express = require("express");
const app = express();
const secret = process.env.DIAFORM_WEBHOOK_SECRET;
function isValidSignature(rawBody, signature) {
if (!Buffer.isBuffer(rawBody) || typeof signature !== "string") {
return false;
}
const expected = Buffer.from(
crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
);
const received = Buffer.from(signature);
return (
received.length === expected.length &&
crypto.timingSafeEqual(received, expected)
);
}
app.post(
"/webhooks/diaform",
express.raw({ type: "application/json" }),
(req, res) => {
if (!isValidSignature(req.body, req.get("X-Webhook-Signature"))) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString("utf8"));
res.sendStatus(200);
// Respond first, then do the slow work.
console.log(event.conversation_id, event.status);
}
);
app.listen(3000);Na túto routu pripojte express.raw(). Ak telo najprv spracuje express.json(), bajty sa zmenia a žiadny podpis nebude sedieť.
Python (Flask)#
import hashlib
import hmac
import json
import os
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ["DIAFORM_WEBHOOK_SECRET"].encode("utf-8")
def is_valid_signature(raw_body, signature):
if not signature:
return False
expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected.encode("utf-8"), signature.encode("utf-8"))
@app.post("/webhooks/diaform")
def diaform_webhook():
raw_body = request.get_data()
if not is_valid_signature(raw_body, request.headers.get("X-Webhook-Signature")):
return "Invalid signature", 401
event = json.loads(raw_body)
print(event["conversation_id"], event["status"])
return "", 200Ak overenie zlyhá#
- Podpisujte nespracované bajty, nie znova serializovaný objekt JSON.
- Použite tajný kľúč endpointu, ktorý požiadavku prijal. Každý endpoint má vlastný.
- Skontrolujte, či uložený tajný kľúč neobsahuje medzery alebo zalomenia riadkov navyše.