Webhooks senden Quiz-Aktivität in dem Moment an deinen eigenen Endpunkt, in dem sie passiert. Diese Referenz listet die Topics, die Felder jedes Payloads und wie die Zustellung funktioniert. Einen Webhook legst du unter Integrations → Developer area → Webhooks an, siehe Webhooks einrichten.
Topics
| Topic | Gesendet, wenn | Eingrenzbar auf |
|---|---|---|
question.answered | jemand eine Quizfrage beantwortet. Ein Aufruf pro gewählter Antwort. | Ein Quiz, eine Frage oder eine Antwort |
result.viewed | jemand die Quiz-Ergebnisse sieht. | Ein Quiz |
profile.updated | ein Profil durch Quiz-Aktivität angelegt oder aktualisiert wird. | Ein Quiz |
email.submitted | jemand eine E-Mail-Frage ausfüllt. | Ein Quiz |
phone.submitted | jemand eine Telefonnummer-Frage ausfüllt. | Ein Quiz |
Wählst du beim Anlegen ein Quiz, eine Frage oder eine Antwort, feuert der Webhook nur dafür. Das Feld topic im Payload enthält das vollständige Topic, zum Beispiel question.answered.<quizId>.<questionId>.<answerId>.
Felder im Payload
Alle Payloads sind JSON mit Feldnamen in camelCase. Jeder Payload enthält topic, advisorId (das Quiz), sessionId und userId (den Besucher). Ausnahme ist profile.updated: Dort steht profileId statt advisorId.
| Topic | Weitere Felder |
|---|---|
question.answered | questionId, questionName, selectedAnswerId, selectedAnswerLabel, selectedAnswerValue, selectedAnswerTextValue, selectedAnswerFormattedValue, isEmail, isPhone, consentAccepted |
result.viewed | email und phoneNumber, falls bekannt; questions[] mit den Antworten jeder Frage (dieselben Antwortfelder wie oben plus selectedAnswerTopic); results[] mit productId, productName, productUrl, productImageUrl und allen Produkteigenschaften |
profile.updated | email, phoneNumber, firstName, lastName, consentAccepted, consentDate, properties[] (Name und Wert), recommendations[] (position, title, url, imageUrl, quizName und die Eigenschaften des Produkts), createdDate, updatedDate, triggerEvent |
email.submitted | questionId, questionName, email, selectedAnswerId, selectedAnswerLabel, consentAccepted |
phone.submitted | questionId, questionName, phoneNumber, selectedAnswerId, selectedAnswerLabel, consentAccepted |
Produkteigenschaften tragen deine Eigenschaftsnamen mit kleinem Anfangsbuchstaben, etwa brand oder skinType. Telefonnummern stehen im internationalen Format, zum Beispiel +31612345678.
{
"topic": "question.answered.3f2c….9a1e….c07b…",
"advisorId": "3f2c…",
"sessionId": "d41a…",
"userId": "7b90…",
"questionId": "9a1e…",
"questionName": "skin_type",
"selectedAnswerId": "c07b…",
"selectedAnswerLabel": "dry",
"selectedAnswerValue": null,
"selectedAnswerTextValue": null,
"selectedAnswerFormattedValue": "dry",
"isEmail": false,
"isPhone": false,
"consentAccepted": null
}Im Studio ist der Beispiel-Payload jedes Topics im Bereich Webhooks nur einen Klick entfernt.

Zustellung
- bluebarry sendet einen
POSTmitContent-Type: application/jsonund allen eigenen Headern, die du beim Webhook hinterlegt hast. - Jedes Ereignis hat einen Header
Idempotency-Key. Eine wiederholte Zustellung nutzt denselben Schlüssel. Speichere ihn also und überspring Schlüssel, die du schon verarbeitet hast. - Jede Anfrage trägt einen Header
bluebarry-signature, mit dem du prüfen kannst, dass sie von bluebarry kommt. Siehe Signaturen prüfen unten. - Jede 2xx-Antwort gilt als zugestellt. Jeder Versuch steht unter Delivery Attempts, wo du den Payload ansehen und erneut senden kannst.
- Antworte mit
410 Gone, um einen Webhook abzuschalten. Ein Webhook, der länger als zwei Tage fehlschlägt, wird automatisch abgeschaltet.
Signaturen prüfen
Jeder Webhook hat sein eigenes Signing Secret, das mit whsec_ beginnt. Studio zeigt es einmal an, direkt nachdem du den Webhook angelegt hast. Wenn du es verlierst, öffne den Bearbeiten-Dialog des Webhooks und klick auf Rotate secret, um ein neues zu bekommen. Das alte Secret funktioniert sofort nicht mehr. Aktualisier deinen Endpunkt also gleich danach.
Der Header bluebarry-signature sieht so aus: t=1700000000,v1=5257a869…. t ist der Sendezeitpunkt in Unix-Sekunden. v1 ist ein HMAC-SHA256 aus dem Zeitstempel, einem Punkt und dem unveränderten Request-Body, mit deinem Signing Secret als Schlüssel, in Hex mit Kleinbuchstaben. So prüfst du eine Anfrage:
- Lies den unveränderten Request-Body, bevor du ihn parst. Wenn du das JSON parst und neu schreibst, ändern sich die Bytes, und die Signatur passt nicht mehr.
- Teil den Header an den Kommas und nimm die Werte von
tundv1. - Berechne HMAC-SHA256 von
{t}.{Body}mit dem vollständigen Signing Secret, inklusivewhsec_, als Schlüssel. - Vergleich das Ergebnis mit
v1in konstanter Zeit. - Lehn die Anfrage ab, wenn
tmehr als fünf Minuten von der Uhr deines Servers abweicht. So kann niemand eine alte Anfrage erneut abspielen.
Eine wiederholte Zustellung wird mit neuem Zeitstempel neu signiert und behält ihren Idempotency-Key.
import crypto from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.BLUEBARRY_WEBHOOK_SECRET; // whsec_…
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("bluebarry-signature") ?? "";
const { t, v1 } = Object.fromEntries(header.split(",").map((part) => part.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${req.body}`)
.digest("hex");
const valid =
typeof v1 === "string" &&
v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
if (!valid || !fresh) return res.sendStatus(401);
const event = JSON.parse(req.body);
// Hier das Ereignis verarbeiten.
res.sendStatus(200);
});