Pfeffersack Webhooks
Mit einem Webhook ruft Pfeffersack eine von dir definierte URL auf und liefert ein signiertes Event, das du in n8n, Zapier oder Make weiterverarbeitest.
invoice.created und invoice.paid löst Pfeffersack derzeit noch nicht aus. Wer auf neue oder bezahlte Rechnungen reagieren will, fragt die Rechnungsliste zeitgesteuert über die REST API ab.Webhook einrichten
- In deinem Workflow-Tool (n8n / Zapier / Make) einen Webhook-Trigger anlegen und dessen URL kopieren.
- In Pfeffersack unter Profil → Integrationen → n8n & Automatisierung auf Webhook hinzufügen klicken, URL einfügen und Events auswählen.
- Das einmalig angezeigte Secret in deinem Workflow speichern. Es wird zur Verifikation der Signatur benötigt.
- Über die Schaltfläche Test senden einen
webhook.test-Event auslösen, um die Verbindung end-to-end zu prüfen.
Request-Format
Jeder Webhook ist ein POST mit Content-Type: application/json. Body-Struktur:
{
"id": "9c1c2b3a-…", // event_id (UUID, stabil über Retries)
"type": "webhook.test", // siehe Event-Tabelle unten
"created_at": "2026-05-12T14:23:11.000Z",
"data": { // pro Event-Typ unterschiedlich
"note": "Pfeffersack Test-Event"
}
}Headers
Pfeffersack sendet folgende Header bei jeder Lieferung:
| Header | Inhalt |
|---|---|
X-Pfeffersack-Signature | t=<unix_seconds>,v1=<hex_hmac>[,v1=<hex_hmac>] |
X-Pfeffersack-Event | Event-Typ (z.B. invoice.paid) |
X-Pfeffersack-Event-Id | UUID des logischen Events (gleich über alle Retries) |
X-Pfeffersack-Delivery | UUID dieses Versuchs (eindeutig pro HTTP-Call) |
X-Pfeffersack-Webhook-Id | UUID deines Webhooks |
User-Agent | Pfeffersack-Webhooks/1.0 |
Signatur-Verifikation
Die Signatur ist ein HMAC-SHA256 über den exakten String ${timestamp}.${rawBody}, signiert mit deinem Webhook-Secret. Receiver sollten in dieser Reihenfolge prüfen:
- Body als raw text (nicht JSON-geparst) extrahieren.
- Header parsen:
t= Timestamp,v1= Signaturen. - Wenn
|now - t| > 300s(Sekunden) → ablehnen (Replay-Schutz). - Erwartete Signatur lokal berechnen:
HMAC_SHA256(secret, t + "." + rawBody). - Wenn keine der gelieferten
v1=-Signaturen timing-safe matched → ablehnen.
Beispiel-Verifikation in Node.js:
import { createHmac, timingSafeEqual } from "crypto";
function verifyPfeffersack(rawBody, sigHeader, secret) {
const parts = Object.fromEntries(
sigHeader.split(",").map((p) => p.split("="))
);
const t = parseInt(parts.t, 10);
if (!Number.isFinite(t)) return false;
if (Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
// Header kann mehrere v1=… enthalten (während einer Secret-Rotation):
const sigs = sigHeader.split(",")
.filter((p) => p.startsWith("v1="))
.map((p) => p.slice(3));
return sigs.some((s) =>
s.length === expected.length &&
timingSafeEqual(Buffer.from(s, "hex"), Buffer.from(expected, "hex"))
);
}Beispiel-Verifikation in PHP (für Make / Zapier Function-Steps):
function verifyPfeffersack(string $rawBody, string $sigHeader, string $secret): bool {
preg_match('/t=(\d+)/', $sigHeader, $tm);
if (empty($tm[1])) return false;
$t = (int) $tm[1];
if (abs(time() - $t) > 300) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
preg_match_all('/v1=([0-9a-f]{64})/', $sigHeader, $matches);
foreach ($matches[1] as $sig) {
if (hash_equals($expected, $sig)) return true;
}
return false;
}Event-Typen
| Type | Wann? | Daten |
|---|---|---|
webhook.test | Manuell via Test senden-Knopf. | { note?: string } |
invoice.created | Für neue Rechnungen vorgesehen (GmbH oder Einzelunternehmen), wird derzeit noch nicht ausgelöst. | { invoice_id, source, invoice_number, invoice_date, amount, currency, customer_id } |
invoice.paid | Für bezahlte Rechnungen vorgesehen, wird derzeit noch nicht ausgelöst. | { invoice_id, source, invoice_number, amount, currency, paid_at } |
Payloads enthalten bewusst nur IDs + Summary. Vollständige Daten holst du anschliessend über die Pfeffersack REST API mit einem API-Key.
Auslieferungs-Verhalten
- Ein Versuch gilt als erfolgreich, wenn dein Endpoint mit HTTP 2xx antwortet — idealerweise innerhalb von 10 Sekunden.
- Sofort-Auslieferung: Ein Event wird direkt gesendet, sobald es entsteht. Heute entsteht ein Event nur über den Knopf Test senden (
webhook.test). Ein Test-Event wird nicht wiederholt; Warteschlange und Wiederholungen unten gelten für Events aus der Warteschlange. - Manuelle Synchronisation: Schlägt die Sofort-Auslieferung fehl (Empfänger gerade down, Netzwerk-Blip, 4xx/5xx), bleibt das Event in der Warteschlange. In der Integrations-UI siehst du dann eine Anzeige „X Lieferungen ausstehend" mit einem Synchronisieren-Knopf, der alle wartenden Events erneut auszuliefern versucht.
- Nach 5 fehlgeschlagenen Versuchen wird das Event als endgültig fehlgeschlagen markiert. Es wird nicht mehr automatisch retried — du kannst es nach Behebung des Receivers mit einem erneuten Klick auf Synchronisieren entfernen lassen (in einer späteren Version; aktuell bleibt es als „failed" im Log sichtbar).
- Nach 20 aufeinanderfolgenden Fehlern wird die Subscription automatisch deaktiviert. Du reaktivierst sie mit dem Toggle in der UI — der Fehlerzähler wird dabei zurückgesetzt.
X-Pfeffersack-Event-Idbleibt über alle Versuche identisch. Receiver müssen dedupen.
Secret-Rotation
Über Secret rotieren generierst du jederzeit ein neues Secret. Während einer 24-Stunden-Grace-Period sendet Pfeffersack jede Lieferung mit beiden Signaturen (neu + alt). So kannst du den Schlüssel in n8n in Ruhe austauschen, ohne dass Lieferungen abreissen.
Sicherheits-Hinweise
- Webhook-URLs müssen
https://verwenden. Interne Adressen (RFC 1918, Loopback, Link-Local, Cloud-Metadata) werden vor jedem Versuch geprüft und blockiert (SSRF-Schutz). - Speichere das Secret nur in n8n / einem Secret-Manager. Es taucht nirgendwo im Pfeffersack-UI nach dem ersten Anzeigen wieder auf.
- Pfeffersack speichert eingegangene HTTP-Antwortbodies bis zu 4 KB im Lieferverlauf — vermeide es daher, vertrauliche Daten in den Response-Body deines Endpoints zu schreiben.
- Dein Endpoint sollte mit
200 OKantworten,bevor du langlaufende Folgearbeit startest. Sonst läuft die 10-Sekunden-Timeout-Grenze ab und der Event wird unnötig retried.
Bereit? Erstelle deinen ersten Webhook unter Profil → Integrationen → n8n & Automatisierung.