Pfeffersack Webhooks
Pfeffersack ruft eine von dir definierte URL auf, sobald in deinem Konto etwas Relevantes passiert — z.B. eine Rechnung erstellt oder bezahlt wird. So lassen sich Pfeffersack-Events ohne eine Zeile Code in n8n, Zapier oder Make weiterverarbeiten.
Webhook einrichten
- In deinem Workflow-Tool (n8n / Zapier / Make) einen Webhook-Trigger anlegen und dessen URL kopieren.
- In Pfeffersack unter Einstellungen → Integrationen → n8n 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": "invoice.paid", // siehe Event-Tabelle unten
"created_at": "2026-05-12T14:23:11.000Z",
"data": { // pro Event-Typ unterschiedlich
"invoice_id": 42,
"source": "invoices",
"invoice_number": "2026-0017",
"amount": 1250,
"currency": "CHF",
"paid_at": "2026-05-12T14:23:11.000Z"
}
}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 | Sobald eine neue Rechnung in Pfeffersack angelegt wird (GmbH oder Einzelunternehmen). | { invoice_id, source, invoice_number, invoice_date, amount, currency, customer_id } |
invoice.paid | Sobald eine Rechnung als bezahlt markiert wird. | { 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: Sobald in Pfeffersack ein Event entsteht (z.B. eine Rechnung wird erstellt), wird der Webhook direkt gesendet — typischerweise innerhalb von einer Sekunde.
- 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 Einstellungen → Integrationen → n8n.