Integrasjoner

Webhooks

Webhooks sender hver avsluttede samtale til serveren din som JSON, med AI-sammendraget og opplysninger om respondenten. Bruk dem til å oppdatere CRM-et, opprette saker eller laste resultatene inn i din egen database uten å eksportere noe for hånd.

Hva som sendes, og når#

Diaform sender én hendelse, conversation.completed. Den sendes når AI-intervjueren avslutter en samtale og sammendraget er klart, vanligvis kort tid etter den siste meldingen.

  • For hele organisasjonen. Hvert endepunkt får hendelser fra alle prosjektene i organisasjonen. Bruk agent_id i payloaden for å skille prosjektene fra hverandre.
  • Avbrutte samtaler sendes også. Hvis en respondent ber om å stoppe, avsluttes samtalen som avbrutt, og du får likevel conversation.completed, med "status": "cancelled". Sjekk status før du behandler et svar som fullført.
  • Forlatte samtaler sendes ikke. Hvis en respondent lukker siden uten at samtalen er avsluttet, sendes ingenting.
  • Forhåndsvisninger i dashbordet sendes ikke. For å teste fullfører du en samtale via den ekte lenken til prosjektet.

Planer og tilganger#

Webhooks er tilgjengelige i alle betalte planer, men ikke i den gratis prøveperioden. Hver plan har en grense for antall endepunkter:

PlanEndepunkter
Starter3
Pro5
Business10
Enterprise25

Bare eiere og administratorer kan legge til, sette på pause eller slette endepunkter. Se Medlemmer og roller og Administrer abonnementet.

Legg til et endepunkt#

Åpne webhooks

Gå til Innstillinger → Webhooks i sidepanelet.

Skriv inn URL-en

Klikk på Legg til endepunkt, lim inn URL-en i dialogen Nytt webhook-endepunkt og klikk på Legg til endepunkt. URL-en må starte med https://.

Lagre hemmeligheten

Dialogen Webhook-hemmelighet viser signeringshemmeligheten din. Klikk på Kopier, lagre den i miljøet på serveren din og klikk deretter på Ferdig.

Hvert endepunkt har sin egen hemmelighet, på formen whsec_ etterfulgt av 43 tegn. Du kan vise eller kopiere den igjen senere fra linjen Signeringshemmelighet på raden til endepunktet.

Sett på pause eller slett et endepunkt#

  • Sett på pause: bytt bryteren på raden til endepunktet fra Aktiv til På pause. Hendelser som skjer mens det står på pause, sendes ikke senere.
  • Slett: klikk på søppelbøtteikonet og deretter på Slett endepunktet for å bekrefte.

Du kan ikke endre URL-en til et endepunkt. For å flytte til en ny URL legger du til et nytt endepunkt, oppdaterer serveren med hemmeligheten til det nye og sletter det gamle.

Levering#

OppførselDetaljer
MetodePOST, JSON-innhold
UtsendingSendes til alle aktive endepunkter samtidig
Tidsavbrudd5 sekunder. Svar med en hvilken som helst 2xx-status før det.
Nye forsøkIngen. Et tidsavbrudd eller et svar som ikke er 2xx, sendes ikke på nytt.
LeveringsloggIkke tilgjengelig i dashbordet
TidspunktHvis behandlingen av en fullført samtale feiler, prøver Diaform behandlingen på nytt hvert 30. minutt, opptil 4 forsøk totalt. Webhooken kan da komme lenge etter at samtalen ble avsluttet.

Svar først og gjør det tunge arbeidet etterpå, for eksempel ved å legge hendelsen i en kø.

Gi Diaform den endelige URL-en. Omdirigeringer følges, men en 301 eller 302 gjør POST om til GET, og innholdet går tapt.

Forespørsel#

HTTP
POST /webhooks/diaform HTTP/1.1
Content-Type: application/json
X-Webhook-Event: conversation.completed
X-Webhook-Signature: 4fa4d553f8abf7a199e5d51ee2401320af843de438e63a2ed6bc33f4f943d906
HeaderVerdi
Content-Typeapplication/json
X-Webhook-Eventconversation.completed
X-Webhook-SignatureHex-kodet HMAC-SHA256 av det rå innholdet, signert med hemmeligheten til endepunktet

Det finnes ingen header for tidsstempel eller leverings-ID. Bruk conversation_id som idempotensnøkkel: lagre ID-ene du har behandlet, og hopp over gjentakelser.

Payload#

JSON
{
  "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"
}

Felt på toppnivå#

FeltTypeBeskrivelse
conversation_idstringID-en til samtalen. Bruk den til å fjerne duplikater.
agent_idstringID-en til prosjektet samtalen hører til
organization_idstringID-en til organisasjonen din
statusstringcompleted eller cancelled
respondentobjectHvem respondenten er, se nedenfor
summaryobjectAI-sammendraget, se nedenfor
completed_atstringISO 8601-tidspunktet da samtalen ble avsluttet

respondent#

FeltTypeBeskrivelse
emailstring eller nullE-postadressen til respondenten, hvis den er kjent
metadataobjectTomt for direkte lenker. For innebygde samtaler inneholder det detaljer om innbyggingen, for eksempel "embed": true og embed_parent_origin.
identitiesarrayAlt som er kjent om respondenten, som type, value og source
identities[].typestringFor eksempel email, name, identifier, user_id, account_id eller company
identities[].sourcestringowner: du oppga den, via en invitasjonslenke eller en identifikator i innbyggingen. ai: hentet ut fra meldingene til respondenten. system: lagt til av Diaform.
invite_idstring eller nullInvitasjons-ID når samtalen kom fra en invitasjonslenke. Se Respondentinvitasjons-API.

summary#

FeltTypeBeskrivelse
respondent_emailnullEldre felt, alltid null. Les e-postadresser fra identities.
respondent_identitiesarrayIdentitetene til respondenten som type og value, uten source
answersarrayÉn oppføring per spørsmål
answers[].question_idstringID-en til spørsmålet i prosjektet ditt
answers[].question_textstringSpørsmålsteksten
answers[].summarystringKort sammendrag av svaret
answers[].sentimentstringpositive, negative, neutral eller mixed
answers[].confidencestringHvor fullstendig spørsmålet ble besvart: high, medium eller low
answers[].quotesarray eller nullSitater fra respondenten som skiller seg ut. null når det ikke finnes noen.
overall_sentimentstringpositive, negative, neutral eller mixed
urgent_issuesarray, null eller manglerKritiske problemer AI-en har flagget
additional_insightsstring, null eller manglerTilbakemeldinger som ikke passer til et bestemt spørsmål

Transkripsjonen og navnet på prosjektet er ikke med. For å se hvordan Diaform samler inn identiteter, se Identifiser respondenter.

Bekreft signaturen#

Sjekk hver forespørsel før du stoler på den. Diaform signerer det nøyaktige innholdet i forespørselen med HMAC-SHA256, med hemmeligheten til endepunktet som nøkkel, og sender den hex-kodede verdien i X-Webhook-Signature.

  1. Les det rå innholdet som bytes, før all JSON-parsing.
  2. Beregn HMAC-SHA256 av disse bytene med hemmeligheten din, som hex.
  3. Avvis forespørselen hvis headeren mangler eller har en annen lengde.
  4. Sammenlign de to verdiene med en funksjon som bruker konstant tid.

Bruk hele hemmeligheten, inkludert prefikset whsec_, som en vanlig UTF-8-streng. Ikke base64-dekod den.

Node.js (Express)#

JavaScript
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);

Monter express.raw() på denne ruten. Hvis express.json() parser innholdet først, endres bytene, og alle signaturer feiler.

Python (Flask)#

Python
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 "", 200

Hvis bekreftelsen feiler#

  • Signer de rå bytene, ikke et JSON-objekt som er serialisert på nytt.
  • Bruk hemmeligheten til endepunktet som tok imot forespørselen. Hvert endepunkt har sin egen.
  • Sjekk om hemmeligheten du lagret, har mellomrom eller linjeskift som ikke hører hjemme der.