# Checkout-Webhooks absichern: API-Key und Signatur in Make, Zapier und n8n

> Rechnungskit-Webhooks in Make, Zapier und n8n absichern: API-Key im richtigen Header, Header Auth, Catch Raw Hook und HMAC-SHA256-Signatur prüfen. Mit konkreten Klickwegen.

Aktualisiert am 08.09.2026

Quelle: https://rechnungskit.de/docs/funktionen/checkout-webhooks

Jeder Checkout-Webhook von Rechnungskit trägt zwei Sicherungen: eine HMAC-SHA256-Signatur im Header X-Rechnungskit-Signature und optional einen von dir gewählten API-Key, der als X-Api-Key und zusätzlich als x-make-apikey mitgeschickt wird. In Make aktivierst du damit die eingebaute API-Key-Authentifizierung, in n8n die Header Auth des Webhook-Knotens, in Zapier vergleichst du den Header per Filter hinter einem Catch Raw Hook.

Mit dem [Rechnungskit-Checkout](https://rechnungskit.de/checkout) verkaufst du über einen Link, die Rechnung entsteht automatisch. Was nach dem Kauf passiert, steuerst du über einen Webhook: Zugang freischalten, Lizenz erzeugen, den Versand anstoßen, in Make, Zapier oder n8n weiterverarbeiten. Diese Seite zeigt, wie du deinen Webhook-Empfänger so einrichtest, dass er nur echte Nachrichten von Rechnungskit akzeptiert.

## Was Rechnungskit sendet {#was-gesendet-wird}

Rechnungskit schickt bei jedem Ergebnis eines Checkouts einen POST-Request mit JSON-Body an deine URL, bei Erfolg als `checkout.paid`, bei Fehlschlag als `checkout.failed`. Jede Nachricht trägt diese Header:

| Header                     | Inhalt                                                                        |
| -------------------------- | ----------------------------------------------------------------------------- |
| `X-Rechnungskit-Signature` | `sha256=<hex>`, die HMAC-SHA256-Signatur des Bodys mit deinem Signatur-Secret |
| `X-Rechnungskit-Event-Id`  | Eindeutige Event-ID für die Dubletten-Erkennung (steht auch im Body)          |
| `X-Api-Key`                | Dein optionaler API-Key, falls beim Anlegen gesetzt                           |
| `x-make-apikey`            | Derselbe API-Key noch einmal, im Header-Namen, den Make nativ prüft           |

Welche Ereignisse ein Endpunkt erhält, wählst du beim Anlegen (und jederzeit später) über die Ereignis-Checkliste am Webhook. Zugestellt wird mindestens einmal: Antwortet dein Empfänger nicht mit einem 2xx-Status, versucht es Rechnungskit bis zu sechsmal mit wachsendem Abstand erneut. Mit dem Test-Button am Webhook prüfst du die ganze Strecke je Ereignis mit einem Beispiel-Event, ohne einen echten Kauf auszulösen.

## Abo-Ereignisse (subscription.*) {#abo-ereignisse}

Verkaufst du über den [Checkout](https://rechnungskit.de/checkout) auch Abonnements, meldet Rechnungskit dir jeden Schritt im Abo-Lebenszyklus als eigenes Ereignis, damit dein System Zugänge automatisch schalten kann:

| Ereignis                      | Wann es feuert                                                                                                                  | Deine typische Reaktion                     |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `subscription.activated`      | Das Abo ist aktiv (erste Zahlung bestätigt oder Test-Phase gestartet)                                                           | Zugang freischalten                         |
| `subscription.charged`        | Eine Abbuchung war erfolgreich (Start, Verlängerung oder Upgrade, siehe `charge.kind`)                                          | Laufzeit verlängern, Beleg ablegen          |
| `subscription.payment_failed` | Eine Abbuchung ist fehlgeschlagen, `charge.attempt`/`charge.maxAttempts`/`charge.nextAttemptAt` zeigen die Wiederholungs-Leiter | Noch NICHT sperren, erst am Ende der Leiter |
| `subscription.updated`        | Plan oder Menge wurden geändert, `change` trägt vorher/nachher                                                                  | Sitzplätze/Features anpassen                |
| `subscription.canceled`       | Eine Kündigung ist eingegangen, wirkt zum Periodenende                                                                          | Vormerken, noch nichts sperren              |
| `subscription.ended`          | Das Abo ist wirklich vorbei (`endedReason`: `period_end`, `immediate` oder `dunning`)                                           | Jetzt Zugang sperren                        |
| `subscription.withdrawn`      | Verbraucherwiderruf nach § 356a BGB                                                                                             | Zugang sperren, Rückzahlung läuft           |

Die `subscription.*`-Nachrichten nutzen eine eigene, **versionierte** Envelope (`"version": 1`): Neben `event`, `eventId` und `occurredAt` steht unter `subscription` immer der komplette aktuelle Zustand (Status, Produkt, Plan, Menge, Betrag, Periode, Käufer, deine Metadaten; gebuchte Add-ons als `addons`-Liste). `change` (bei Änderungen) und `charge` (bei Abbuchungen) kommen als eigene Blöcke dazu. Verlasse dich auf den absoluten Zustand, nicht auf die Reihenfolge der Nachrichten: Zugestellt wird mindestens einmal und ohne Reihenfolge-Garantie; erkenne Dubletten an der `eventId` und nimm bei `subscription.updated` den Stand mit dem jüngsten `occurredAt`.

## Werbe-Attribution im Payload (Klick-IDs und UTM)

Landet ein Käufer mit Werbe-Parametern auf dem Checkout (`fbclid`, `gclid`, `msclkid`, `ttclid` oder `utm_source` bis `utm_term`), friert Rechnungskit sie am Kauf ein und liefert sie in jeder Webhook-Nachricht mit: bei `checkout.*` als zusätzliches Feld `attribution` am Ende des Payloads, bei `subscription.*` innerhalb von `subscription`. Das Feld ist `null`, wenn der Käufer ohne Werbe-Parameter kam. Damit kannst du Conversions serverseitig an Werbeplattformen melden (etwa an die Meta Conversions API, mit `fbclid` für die Zuordnung zur Kampagne) oder deine Kanäle selbst auswerten. Die bisherige Feldreihenfolge vor dem neuen Feld bleibt unverändert, bestehende Signatur-Prüfungen laufen weiter. Zusätzlich hängt Rechnungskit dieselben Parameter (plus `rk_order`, `rk_event_id`, `rk_amount_cents`, `rk_currency`; `rk_event_id` ist die Event-ID, die auch die native Meta-Integration verwendet, für die Dubletten-Erkennung mit einem eigenen Browser-Pixel) an die Weiterleitung zur eigenen Danke-Seite an, wenn du am Produkt eine Return-URL hinterlegt hast, damit dort dein eigenes Conversion-Tag feuern kann.

Die Absicherung ist identisch zu den Checkout-Ereignissen: derselbe Endpunkt, derselbe API-Key, dieselbe Signatur. Ein Endpunkt kann Checkout- und Abo-Ereignisse gleichzeitig abonnieren.

## Zwei Sicherungswege: API-Key und Signatur {#zwei-wege}

Deine Webhook-URL ist bereits die erste Sicherung: Sie enthält einen langen Zufallsanteil und ist praktisch nicht erratbar. Wer es genauer möchte, hat zwei Stufen:

1. **API-Key (einfach):** Beim Anlegen des Webhooks in Rechnungskit trägst du einen frei gewählten Key ein. Er wird bei jeder Zustellung als Header mitgeschickt. Dein Empfänger vergleicht nur noch den Wert, ganz ohne Kryptografie. Das ist der richtige Weg für Make, Zapier und n8n.
2. **HMAC-Signatur (stark):** Rechnungskit signiert den exakten JSON-Body mit deinem Signatur-Secret (`whsec_…`, wird beim Anlegen genau einmal angezeigt). Wer die Signatur nachrechnet, weiß sicher, dass die Nachricht von Rechnungskit stammt und nicht verändert wurde. Das ist der richtige Weg für eigenen Code und für Empfänger mit Krypto-Unterstützung.

## Make: API-Key-Authentifizierung {#make}

Make prüft API-Keys nativ am Custom Webhook und erwartet den Key im Header `x-make-apikey`. Genau dorthin schickt Rechnungskit deinen Key automatisch, es ist also keine Umbenennung oder Zwischenlogik nötig.

1. Lege in deinem Szenario das Modul **Webhooks, Custom webhook** an (oder öffne deinen bestehenden Webhook) und öffne die erweiterten Einstellungen des Webhooks.
2. Klicke bei **API Key authentication** auf **Add API key** und lege einen neuen Schlüsselbund (Keychain) an. Trage dort exakt den Key ein, den du in Rechnungskit beim Anlegen des Webhooks als API-Key gesetzt hast. Make zeigt den Wert danach aus Sicherheitsgründen nicht mehr an.
3. Speichere. Ab jetzt beantwortet Make jede Nachricht ohne passenden Key mit HTTP 401, dein Szenario startet nur noch für echte Rechnungskit-Events.

<!-- Screenshot: /docs/webhooks/make-api-key.png: Make: Custom Webhook, API Key authentication mit Keychain -->

Zwei nützliche Extras in denselben Webhook-Einstellungen: **IP restrictions** (eine kommagetrennte Liste erlaubter Absender-IPs) und **Get request headers**, falls du Header-Werte im Szenario weiterverwenden möchtest. Die HMAC-Signatur lässt sich in Make ebenfalls nachrechnen (die Funktion `sha256` akzeptiert einen Key-Parameter und liefert dann ein HMAC), dafür müssen aber JSON-Pass-through und Header-Übernahme aktiv sein und ein Filter die Werte vergleichen. Für Make empfehlen wir den API-Key.

## n8n: Header Auth {#n8n}

Der Webhook-Knoten von n8n bringt die Header-Prüfung als eigene Authentifizierungsart mit:

1. Öffne deinen **Webhook**-Knoten und stelle **Authentication** auf **Header Auth**.
2. Lege unter **Credential for Header Auth** eine neue Credential vom Typ Header Auth an. Als **Name** trägst du `X-Api-Key` ein, als **Value** deinen in Rechnungskit gesetzten API-Key. Die Groß- und Kleinschreibung des Header-Namens spielt keine Rolle.
3. Speichere und aktiviere den Workflow. Nachrichten ohne passenden Key lehnt n8n mit HTTP 403 ab.

<!-- Screenshot: /docs/webhooks/n8n-header-auth.png: n8n: Webhook-Node, Authentication Header Auth mit Credential -->

Wenn du stattdessen die Signatur prüfen willst: Aktiviere am Webhook-Knoten die Option **Raw Body**, damit der exakte Body erhalten bleibt. Danach berechnet entweder der **Crypto**-Knoten (Action **Hmac**, Type **SHA256**, Encoding **HEX**, Secret = dein `whsec_…`) oder ein **Code**-Knoten mit `require('crypto')` die Signatur, und ein **IF**-Knoten vergleicht `"sha256=" + hmac` mit dem Header `x-rechnungskit-signature`.

## Zapier: Catch Raw Hook und Filter {#zapier}

Zapier hat keine eingebaute Key-Prüfung am Webhook-Trigger; die unerratbare Catch-URL ist dort die Standard-Sicherung. Wenn du zusätzlich den API-Key prüfen willst, brauchst du den Trigger-Typ mit Headern:

1. Verwende als Trigger **Webhooks by Zapier** mit dem Event **Catch Raw Hook** (nicht Catch Hook: der normale Catch Hook parst nur den Body und liefert keine Header). Catch Raw Hook setzt einen kostenpflichtigen Zapier-Plan voraus.
2. Löse ein Test-Event aus ("Test-Ereignis senden" in Rechnungskit), damit Zapier die Struktur kennt. Die Header erscheinen mit Unterstrichen, dein Key steht also im Feld `headers X_Api_Key`.
3. Füge direkt nach dem Trigger einen **Filter by Zapier** ein: Only continue if, Feld `X_Api_Key`, Bedingung **(Text) Exactly matches**, Wert = dein API-Key.

<!-- Screenshot: /docs/webhooks/zapier-filter.png: Zapier: Catch Raw Hook und Filter auf X_Api_Key -->

Auch die Signatur lässt sich in Zapier prüfen: Der Catch Raw Hook liefert den unveränderten Body im Feld `raw`, ein **Code by Zapier**-Schritt (Python mit `hmac`/`hashlib` oder JavaScript mit `crypto`) berechnet daraus die Signatur und gibt ein Vergleichsergebnis aus, auf das der Filter prüft. Für die meisten Zaps ist der API-Key-Filter der pragmatische Weg.

## Die Signatur selbst prüfen (HMAC-SHA256) {#signatur}

Für eigene Endpunkte (dein Server, eine Cloud Function, der n8n-Code-Knoten) rechnest du die Signatur über den **unveränderten** Request-Body nach, mit dem Signatur-Secret aus der Webhook-Einrichtung:

```js
const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
	const expected =
		'sha256=' + crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
	return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}
```

Drei Stolpersteine aus der Praxis: Erstens muss wirklich der rohe Body gehasht werden, nicht ein neu serialisiertes JSON (schon eine andere Feldreihenfolge ändert die Signatur). Zweitens ist der Vergleich mit `timingSafeEqual` dem einfachen `===` vorzuziehen. Drittens lohnt sich die Dubletten-Erkennung über die Event-ID: Rechnungskit stellt mindestens einmal zu, bei Wiederholungen kommt dieselbe `X-Rechnungskit-Event-Id` erneut.

## Häufige Fragen

### Brauche ich API-Key und Signatur gleichzeitig?

Nein. Der API-Key ist der einfache Weg für No-Code-Werkzeuge und für die meisten Fälle völlig ausreichend, zumal deine Webhook-URL ohnehin nicht erratbar ist. Die Signatur ist der kryptografisch stärkere Nachweis, dass die Nachricht wirklich von Rechnungskit stammt und unterwegs nicht verändert wurde. Beide Header werden immer mitgeschickt, du entscheidest empfangsseitig, was du prüfst.

### Wo lege ich den API-Key in Rechnungskit fest?

Beim Anlegen des Webhooks unter Einstellungen, Produkte im Feld "API-Key (optional)". Der Key wird verschlüsselt gespeichert und danach nicht mehr angezeigt. Um ihn zu ändern, legst du den Webhook neu an.

### Warum sehe ich das Signatur-Secret nur einmal?

Das Secret wird wie ein Passwort behandelt: Es wird verschlüsselt gespeichert und nach der Erstellung nie wieder ausgeliefert. Kopiere es direkt beim Anlegen in dein Zielsystem. Geht es verloren, legst du den Webhook einfach neu an und erhältst ein neues Secret.

### Was passiert, wenn mein Empfänger den Webhook ablehnt?

Rechnungskit stellt jede Nachricht bis zu sechsmal mit wachsendem Abstand zu (beginnend bei 15 Sekunden, verdoppelnd bis maximal eine Stunde). Jeder Versuch wird serverseitig mit Status-Code und Fehlertext protokolliert; bei hartnäckigen Zustellproblemen hilft dir der Support mit dem Protokoll weiter. Antwortet dein Empfänger mit einem 2xx-Status, gilt die Nachricht als zugestellt.

### Welche Ereignisse und Daten enthält der Webhook?

Für Einmalkäufe gibt es checkout.paid und checkout.failed; der JSON-Body enthält unter anderem Produkt, Betrag und Währung, E-Mail und Name des Käufers, das Lieferland, die Rechnungsnummer (sobald erstellt) und deine durchgereichten Metadaten. Für Abos gibt es sieben eigene Ereignisse (subscription.activated bis subscription.withdrawn) mit einer versionierten Envelope, die immer den kompletten Abo-Zustand trägt. Eine eindeutige Event-ID steht jeweils im Body und im Header X-Rechnungskit-Event-Id, damit du doppelte Zustellungen erkennen kannst.

## Quellen

- [Make: Webhooks-Dokumentation (API key, x-make-apikey, IP restrictions)](https://apps.make.com/gateway)
- [Make: Text- und Binärfunktionen (sha256 mit Key = HMAC)](https://help.make.com/text-and-binary-functions)
- [n8n: Webhook-Node (Authentication, Raw Body)](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/)
- [n8n: Crypto-Node (Hmac, SHA256)](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.crypto/)
- [Zapier: Trigger Zaps from webhooks (Catch Hook vs. Catch Raw Hook)](https://help.zapier.com/hc/en-us/articles/8496288690317-Trigger-Zaps-from-webhooks)
