Die Ereignisse
Ein Webhook ist eine Adresse deines Servers, an die wir ein Ereignis schicken, sobald es passiert. Sechs Ereignisse gibt es:
form.response.created Eine neue Antwort auf eines deiner Formulare
form.response.updated Jemand hat seine Antwort über den Bearbeitungslink geändert
order.paid Eine Bestellung ist bezahlt
order.fulfilled Eine Bestellung ist versendet oder übergeben
order.canceled Eine bestätigte Bestellung wurde storniert
order.refunded Eine Bestellung wurde erstattetorder.refunded kommt bei der ersten Teilerstattung und bei der vollständigen Erstattung. Eine Kasse, die nie bezahlt wurde und verfällt, ist kein Storno — dafür kommt kein Ereignis.
Einen Webhook einrichten
Programme können Webhooks auch selbst eintragen: POST /v1/webhooks mit einem Schlüssel, der webhooks:manage trägt — und dazu das Leserecht der Daten, die der Webhook schickt (forms:read für Formular-Ereignisse, orders:read für Bestellungen).
- Öffne im Dashboard den Bereich Entwickler.
- Trage unter „Webhooks“ die Adresse deines Empfängers ein — sie beginnt mit https.
- Wähle unter „Welche Ereignisse?“ aus, was du bekommen willst, und klicke auf „Webhook anlegen“.
- Kopiere das Geheimnis, das danach erscheint, in deinen Empfänger.
- Klicke auf „Test senden“ — das Ergebnis steht sofort darunter.
Was ankommt
Jedes Ereignis ist ein POST mit JSON. In data.object steht das Objekt in derselben Form wie in der API — eine Bestellung wie bei GET /v1/orders/{id}, eine Antwort wie in der Liste der Antworten. Bei Formular-Ereignissen steht in data.form zusätzlich, zu welchem Formular die Antwort gehört.
{
"id": "evt_Q2x9kLmP4vT7aB1cD3eF5gH7",
"object": "event",
"type": "order.paid",
"api_version": "2026-09-23",
"created_at": "2026-09-23T14:05:12Z",
"data": {
"object": {
"id": 5531,
"object": "order",
"number": 42,
"status": "confirmed",
"payment_status": "paid",
"currency": "EUR",
"total": 3890,
"customer": { "email": "mara@example.com", "name": "Mara Sol" },
"items": [
{ "name": "Kaffeebohnen, 500 g", "sku": "KAF-500", "quantity": 2, "unit_price": 1690 }
]
}
}
}Die Signatur prüfen
Jeder Aufruf trägt die Kopfzeile KENJTEC-Signature: t=<Zeit>,v1=<Signatur>. Die Signatur ist ein HMAC-SHA256 über die Zeit, einen Punkt und den unveränderten Körper der Anfrage — mit dem Geheimnis deines Webhooks als Schlüssel. Verwirf alles, dessen Zeit mehr als fünf Minuten zurückliegt.
Prüfe gegen den rohen Körper, bevor du das JSON einliest: Schon ein anderes Leerzeichen ergibt eine andere Signatur. Das Geheimnis findest du jederzeit unter „Geheimnis zeigen“; „Geheimnis erneuern“ macht das alte sofort ungültig.
import crypto from "node:crypto";
// raw: the unparsed request body (Buffer), header: the KENJTEC-Signature header
export function verify(raw, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${parts.t}.`).update(raw).digest("hex");
const given = String(parts.v1 ?? "");
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}import hashlib, hmac, time
def verify(raw: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Dasselbe Ereignis kann zweimal ankommen — etwa wenn dein Server antwortet, die Antwort uns aber nicht erreicht. Die Kennung in id (auch in der Kopfzeile KENJTEC-Event-Id) bleibt dabei gleich: Merke sie dir und verarbeite jedes Ereignis nur einmal.
Wenn etwas nicht ankommt
Als angekommen gilt jede Antwort mit einem Status von 200 bis 299 innerhalb von zehn Sekunden. Weiterleitungen folgen wir nicht.
Kommt ein Ereignis nicht an, versuchen wir es erneut: nach 1, 5 und 30 Minuten, nach 2, 6 und 12 Stunden und zweimal nach 24 Stunden — gut drei Tage lang. Kommen fünf Ereignisse in Folge endgültig nicht an, schalten wir den Webhook ab; im Dashboard steht dann, warum, und „Wieder einschalten“ setzt ihn zurück.
Unter „Protokoll“ siehst du jede Zustellung der letzten 30 Tage mit Status und Inhalt. Von dort lässt sich jedes Ereignis mit „Erneut senden“ noch einmal schicken.

Webhooks gehen nur an öffentliche https-Adressen. Adressen in internen Netzen und Adressen von KENJTEC selbst nehmen wir nicht an.