Webhookovi
Webhookovi šalju svaki završeni razgovor na vaš poslužitelj kao JSON, s AI sažetkom i podacima o ispitaniku. Pomoću njih ažurirajte CRM, otvarajte tikete ili učitavajte rezultate u vlastitu bazu podataka bez ručnog izvoza.
Što se šalje i kada#
Diaform šalje jedan događaj, conversation.completed. Pokreće se kad AI intervjuer završi razgovor i sažetak je spreman, obično nekoliko trenutaka nakon zadnje poruke.
- Za cijelu organizaciju. Svaka krajnja točka prima događaje iz svih projekata u vašoj organizaciji. Projekte razlikujte po polju
agent_idu payloadu. - Šalju se i otkazani razgovori. Ako ispitanik zatraži prekid, razgovor završava kao otkazan, a vi i dalje dobivate
conversation.completed, uz"status": "cancelled". Provjeritestatusprije nego što odgovor smatrate dovršenim. - Napušteni razgovori ne šalju se. Ako ispitanik zatvori stranicu, a razgovor nije završio, ništa se ne šalje.
- Pregledi na nadzornoj ploči ne šalju se. Za testiranje dovršite razgovor putem stvarne poveznice projekta.
Paketi i dopuštenja#
Webhookovi su dostupni u svim plaćenim paketima, ali ne tijekom besplatnog probnog razdoblja. Svaki paket ima ograničenje broja krajnjih točaka:
| Paket | Krajnje točke |
|---|---|
| Starter | 3 |
| Pro | 5 |
| Business | 10 |
| Enterprise | 25 |
Krajnje točke mogu dodavati, pauzirati i brisati samo vlasnici i administratori. Pogledajte Članovi i uloge i Upravljanje pretplatom.
Dodavanje krajnje točke#
Otvorite webhookove
Na bočnoj traci idite na Postavke → Webhookovi.
Unesite URL
Kliknite Dodaj krajnju točku, zalijepite URL u dijalog Nova krajnja točka webhooka i kliknite Dodaj krajnju točku. URL mora počinjati s https://.
Spremite tajni ključ
Dijalog Tajni ključ webhooka prikazuje vaš tajni ključ za potpisivanje. Kliknite Kopiraj, spremite ga u okruženje svojeg poslužitelja, a zatim kliknite Gotovo.
Svaka krajnja točka ima vlastiti tajni ključ u obliku whsec_ nakon čega slijede 43 znaka. Kasnije ga možete ponovno prikazati ili kopirati preko stavke Tajni ključ za potpisivanje u retku krajnje točke.
Pauziranje ili brisanje krajnje točke#
- Pauziranje: prebacite prekidač na retku krajnje točke s Aktivna na Pauzirana. Događaji koji se dogode dok je pauzirana ne šalju se naknadno.
- Brisanje: kliknite ikonu koša za smeće, a zatim za potvrdu Izbriši krajnju točku.
URL krajnje točke ne možete uređivati. Da biste prešli na novi URL, dodajte novu krajnju točku, ažurirajte poslužitelj njezinim tajnim ključem, a zatim izbrišite staru.
Isporuka#
| Ponašanje | Pojedinosti |
|---|---|
| Metoda | POST, tijelo u formatu JSON |
| Raspodjela | Šalje se svim aktivnim krajnjim točkama istodobno |
| Vremensko ograničenje | 5 sekundi. Prije toga odgovorite bilo kojim statusom 2xx. |
| Ponovni pokušaji | Nema ih. Istek vremena ili odgovor koji nije 2xx ne šalje se ponovno. |
| Zapisnik isporuka | Nije dostupan na nadzornoj ploči |
| Vrijeme | Ako obrada dovršenog razgovora ne uspije, Diaform ponavlja obradu svakih 30 minuta, najviše 4 pokušaja ukupno. Webhook tada može stići dugo nakon završetka razgovora. |
Najprije odgovorite, a sporiji posao obavite poslije, npr. tako da događaj stavite u red čekanja.
Diaform treba dobiti konačni URL. Preusmjeravanja se slijede, ali 301 ili 302 pretvara POST u GET i tijelo se gubi.
Zahtjev#
POST /webhooks/diaform HTTP/1.1
Content-Type: application/json
X-Webhook-Event: conversation.completed
X-Webhook-Signature: 4fa4d553f8abf7a199e5d51ee2401320af843de438e63a2ed6bc33f4f943d906| Zaglavlje | Vrijednost |
|---|---|
Content-Type | application/json |
X-Webhook-Event | conversation.completed |
X-Webhook-Signature | Heksadecimalni HMAC-SHA256 neobrađenog tijela, potpisan tajnim ključem krajnje točke |
Nema zaglavlja s vremenskom oznakom ni ID-jem isporuke. Kao ključ idempotentnosti koristite conversation_id: spremajte ID-jeve koje ste obradili i preskačite ponavljanja.
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"
}Polja na najvišoj razini#
| Polje | Vrsta | Opis |
|---|---|---|
conversation_id | string | ID razgovora. Koristite ga za uklanjanje duplikata. |
agent_id | string | ID projekta kojem razgovor pripada |
organization_id | string | ID vaše organizacije |
status | string | completed ili cancelled |
respondent | object | Tko je ispitanik, pogledajte u nastavku |
summary | object | AI sažetak, pogledajte u nastavku |
completed_at | string | Vrijeme završetka razgovora u formatu ISO 8601 |
respondent#
| Polje | Vrsta | Opis |
|---|---|---|
email | string ili null | E-adresa ispitanika, ako je poznata |
metadata | object | Prazno za izravne poveznice. Za ugrađene razgovore sadrži podatke o ugradnji poput "embed": true i embed_parent_origin. |
identities | array | Sve što se zna o ispitaniku, kao type, value i source |
identities[].type | string | Na primjer email, name, identifier, user_id, account_id ili company |
identities[].source | string | owner: vi ste ga dali, putem pozivne poveznice ili identifikatora iz ugradnje. ai: izdvojen iz poruka ispitanika. system: dodao ga je Diaform. |
invite_id | string ili null | ID pozivnice kad je razgovor započeo putem pozivne poveznice. Pogledajte API za pozivnice ispitanika. |
summary#
| Polje | Vrsta | Opis |
|---|---|---|
respondent_email | null | Zastarjelo polje, uvijek null. E-adrese čitajte iz identities. |
respondent_identities | array | Identiteti ispitanika kao type i value, bez source |
answers | array | Jedan unos po pitanju |
answers[].question_id | string | ID pitanja u vašem projektu |
answers[].question_text | string | Tekst pitanja |
answers[].summary | string | Kratak sažetak odgovora |
answers[].sentiment | string | positive, negative, neutral ili mixed |
answers[].confidence | string | Koliko je potpuno pitanje odgovoreno: high, medium ili low |
answers[].quotes | array ili null | Istaknuti citati ispitanika. null kad ih nema. |
overall_sentiment | string | positive, negative, neutral ili mixed |
urgent_issues | array, null ili izostavljeno | Kritični problemi koje je AI označio |
additional_insights | string, null ili izostavljeno | Povratne informacije koje se ne uklapaju ni u jedno pitanje |
Transkript i naziv projekta nisu uključeni. Kako Diaform prikuplja identitete, pročitajte u članku Prepoznavanje ispitanika.
Provjera potpisa#
Provjerite svaki zahtjev prije nego što mu povjerujete. Diaform potpisuje točno tijelo zahtjeva algoritmom HMAC-SHA256, s tajnim ključem krajnje točke kao ključem, i šalje heksadecimalni sažetak u zaglavlju X-Webhook-Signature.
- Pročitajte neobrađeno tijelo kao bajtove, prije bilo kakvog parsiranja JSON-a.
- Izračunajte HMAC-SHA256 tih bajtova sa svojim tajnim ključem, u heksadecimalnom obliku.
- Odbijte zahtjev ako zaglavlje nedostaje ili ima drukčiju duljinu.
- Usporedite dvije vrijednosti funkcijom koja radi u konstantnom vremenu.
Koristite cijeli tajni ključ, uključujući prefiks whsec_, kao običan UTF-8 niz. Nemojte ga dekodirati iz 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);Postavite express.raw() na ovu rutu. Ako express.json() prvi parsira tijelo, bajtovi se mijenjaju i nijedan potpis ne prolazi.
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 "", 200Ako provjera ne uspije#
- Potpisujte neobrađene bajtove, a ne ponovno serijalizirani JSON objekt.
- Koristite tajni ključ krajnje točke koja je primila zahtjev. Svaka krajnja točka ima vlastiti.
- Provjerite ima li u spremljenom tajnom ključu suvišnih razmaka ili prijeloma redaka.