Zum Inhalt springen
Rechnungskit

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

Aktualisiert am

Jeden Checkout-Webhook von Rechnungskit sicherst du mit einem von dir gewählten API-Key ab, der als X-Api-Key und zusätzlich als x-make-apikey mitgeschickt wird. Auf Wunsch kommt eine HMAC-SHA256-Signatur im Header X-Rechnungskit-Signature dazu. 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 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.

Zustellung · mindestens einmal
Vom Kauf bis zur angenommenen Nachricht
Ereignis
checkout.paid / checkout.failedEinmalkauf erfolgreich oder fehlgeschlagen
subscription.*sieben Abo-Ereignisse
Rechnungskit sendet
  1. POST mit JSON-Bodyan deine URL
  2. HeaderEvent-ID, dazu API-Key und Signatur, wenn eingerichtet
Dein Empfänger
2xxzugestellt
anderer Status oder keine Antwortneuer Versuch nach 15 s, dann doppelt so lange, höchstens 1 Stunde, insgesamt bis zu sechs Versuche
Bei Wiederholungen kommt dieselbe Event-ID erneut. Erkenne Dubletten daran.

Was Rechnungskit sendet

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. Die Nachricht trägt diese Header (Signatur und API-Key nur, wenn du sie beim Anlegen eingerichtet hast):

Header Inhalt
X-Rechnungskit-Signature sha256=<hex>, die HMAC-SHA256-Signatur des Bodys, falls eingeschaltet
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. Mit dem Test-Button am Webhook prüfst du die ganze Strecke je Ereignis mit einem Beispiel-Event, ohne einen echten Kauf auszulösen.

Zugestellt wird mindestens einmal. Antwortet dein Empfänger nicht mit einem 2xx-Status, versucht es Rechnungskit erneut, insgesamt bis zu sechsmal und mit wachsendem Abstand.

Abo-Ereignisse (subscription.*)

Verkaufst du über den Checkout auch Abonnements, meldet Rechnungskit dir jeden Schritt im Abo-Lebenszyklus als eigenes Ereignis. So kann dein System Zugänge automatisch schalten:

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 über den Widerrufsbutton (§ 356a BGB) Zugang sperren, Rückzahlung läuft
Abo-Lebenszyklus
Wann du den Zugang sperrst
  1. subscription.activatedZugang freischalten
  2. subscription.payment_failednoch nicht sperrenerst am Ende der Leiter
  3. subscription.canceledvormerkenwirkt zum Periodenende
  4. subscription.endedjetzt sperrenbei subscription.withdrawn ebenfalls

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 (etwa die workspace_id, die du beim Abo-Link per API mitgibst) und gebuchte Add-ons als addons-Liste. Den Plan nennen drei Felder: plan (Name), planId (interne Id) und planSlug, das Kürzel aus dem Abo-Link (der Teil nach /s/). Bei einem Planwechsel trägt change.plan neben from und to auch fromSlug und toSlug. Interne Felder von Rechnungskit stehen nicht in den Metadaten. change (bei Änderungen) und charge (bei Abbuchungen) kommen als eigene Blöcke dazu.

Testkäufe erkennst du am Feld test: Es steht bei checkout.* am Ende des Payloads und bei subscription.* auf der obersten Ebene der Envelope. "test": true heißt, der Kauf lief im Testmodus oder über deine Test-Zugangsdaten, es ist kein echtes Geld geflossen. Bei echten Käufen steht dort false.

Verlass 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.

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

Rechnung bezahlt (invoice.paid)

invoice.paid meldet, dass eine Rechnung bei Kauf auf Rechnung oder Vorkasse vollständig bezahlt ist. Das gilt für Bestellungen über die API und über den Checkout. Typischer Einsatz: Deine Software schaltet den Zugang frei, sobald das Geld da ist.

Das Ereignis kommt, sobald der offene Posten auf „bezahlt“ springt:

  • du bestätigst die Zahlung in der App unter Offene Posten, auch mit der letzten Teilzahlung,
  • eine zugeordnete Überweisung (Stripe-Überweisung) deckt den Betrag,
  • dein Shop meldet die Bestellung als bezahlt,
  • ein Guthaben oder eine Gutschrift deckt den Rest.

Wurde eine Vorkasse schon vor der Rechnung bezahlt, kommt invoice.paid direkt mit der Rechnung. Je Rechnung und Endpunkt kommt das Ereignis genau einmal. Webhooks mit der Auswahl „Alle Produkte“ oder „Alle Einmalkauf-Produkte“ erhalten es, Abo-Webhooks nicht.

{
  "event": "invoice.paid",
  "version": 1,
  "eventId": "…",
  "occurredAt": "2026-10-11T09:30:00.000Z",
  "invoice": { "id": "…", "number": "RE-2026-0042", "issueDate": "2026-10-10", "grossCents": 34510, "paidCents": 34510, "currency": "EUR" },
  "order": { "orderNo": "ORDER-1", "paymentRef": null, "source": "api" },
  "buyer": { "email": "kunde@example.com", "name": "Kunde GmbH" },
  "test": false
}

Denselben Stand liefert die API im Block payment von GET /v1/invoices, falls du lieber abfragst.

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) und stimmt in der Einwilligungs-Leiste zu, friert Rechnungskit sie am Kauf ein. Sie stehen dann in jeder Webhook-Nachricht:

Wo Feld
checkout.* zusätzliches Feld attribution am Ende des Payloads
subscription.* attribution innerhalb von subscription
Weiterleitung zur Danke-Seite (Return-URL am Produkt) dieselben Parameter plus rk_order, rk_event_id, rk_amount_cents, rk_currency

Das Feld ist null, wenn der Käufer ohne Werbe-Parameter kam, abgelehnt oder gar nicht entschieden hat. Ohne Einwilligung bekommt die Return-URL nur die rk_-Parameter, keine Klick-IDs und keine UTM-Parameter.

Wann die Leiste erscheint

Die Leiste erscheint nur, wenn im Projekt eine Tracking-Integration aktiv ist (Meta Ads, Google-Tag oder TikTok-Pixel) und eine Datenschutz-URL hinterlegt ist. Ohne sie bleibt das Feld immer null, denn Klick-IDs und UTM-Parameter werden nur mit Einwilligung gespeichert (§ 25 Abs. 1 TDDDG, EDSA-Leitlinien 2/2023, Rn. 50 f.).

Ob gesendet wird, prüft Rechnungskit beim Senden an der gespeicherten Einwilligung. Bestellungen von vor der Umstellung (Oktober 2026) haben keinen Nachweis und kommen ohne Attribution.

Wofür du die Daten nutzt

Damit kannst du Conversions serverseitig an Werbeplattformen melden, etwa an die Meta Conversions API mit fbclid für die Zuordnung zur Kampagne. Oder du wertest deine Kanäle selbst aus.

Die Parameter an der Return-URL sind dafür da, dass auf deiner Danke-Seite dein eigenes Conversion-Tag feuern kann. rk_event_id ist die Event-ID, die auch die native Meta-Integration verwendet. Darüber erkennt Meta Dubletten mit einem eigenen Browser-Pixel.

Die bisherige Feldreihenfolge vor dem neuen Feld bleibt unverändert, bestehende Signatur-Prüfungen laufen weiter.

Zwei Sicherungswege: API-Key und Signatur

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): Schaltest du beim Anlegen „HMAC-Signatur zusätzlich senden“ ein, signiert Rechnungskit den exakten JSON-Body mit einem Signatur-Secret (whsec_…, wird nach dem Speichern 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.
Absicherung im Vergleich
Drei Stufen, eine reicht meist
Unerratbare URL
Langer Zufallsanteil in der Adresse. Immer dabei, ohne Einrichtung
Standardin Zapier die übliche Sicherung
API-Key
Header X-Api-Key und x-make-apikey. Der Empfänger vergleicht nur den Wert
FürMake, n8n, Zapier
HMAC-Signatur
Header X-Rechnungskit-Signature über den exakten Body, mit Secret whsec_…
Füreigenen Code
Mitgeschickt wird, was du beim Anlegen eingerichtet hast. Was du davon prüfst, entscheidest du im Empfänger.

Die drei Werkzeuge im Überblick

Werkzeug Wo du den Key prüfst Header Ohne passenden Key
Make Custom webhook, API Key authentication x-make-apikey HTTP 401
n8n Webhook-Knoten, Authentication Header Auth X-Api-Key HTTP 403
Zapier Catch Raw Hook plus Filter by Zapier X_Api_Key (Zapier schreibt Unterstriche) der Zap läuft nicht weiter

Make: API-Key-Authentifizierung

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.

Zwei nützliche Extras in denselben Webhook-Einstellungen:

  • IP restrictions: eine kommagetrennte Liste erlaubter Absender-IPs.
  • 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 muss die Werte vergleichen. Für Make empfehlen wir den API-Key.

n8n: Header Auth

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.

Willst du stattdessen die Signatur prüfen, 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. Ein IF-Knoten vergleicht dann "sha256=" + hmac mit dem Header x-rechnungskit-signature.

Zapier: Catch Raw Hook und Filter

Zapier hat keine eingebaute Key-Prüfung am Webhook-Trigger, die unerratbare Catch-URL ist dort die Standard-Sicherung. Willst du zusätzlich den API-Key prüfen, 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.

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)

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:

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:

  1. Roher Body: Es muss wirklich der rohe Body gehasht werden, nicht ein neu serialisiertes JSON. Schon eine andere Feldreihenfolge ändert die Signatur.
  2. Sicherer Vergleich: timingSafeEqual ist dem einfachen === vorzuziehen.
  3. Dubletten: Die Erkennung über die Event-ID lohnt sich. 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.

Mitgeschickt wird, was du beim Anlegen eingerichtet hast: der API-Key, wenn du einen eingetragen hast, und die Signatur, wenn der Schalter „HMAC-Signatur zusätzlich senden“ an ist. Was du davon prüfst, entscheidest du im Empfänger.

Wo lege ich den API-Key in Rechnungskit fest?

Beim Anlegen des Webhooks unter Verbindungen → API im Abschnitt Checkout-Webhooks, Feld „API-Key: dein Credential für n8n / Make (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 nach dem Speichern 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 versucht jede Nachricht bis zu sechsmal zuzustellen, mit wachsendem Abstand: beginnend bei 15 Sekunden, verdoppelnd bis maximal eine Stunde. Antwortet dein Empfänger mit einem 2xx-Status, gilt die Nachricht als zugestellt.

Jeder Versuch wird serverseitig mit Status-Code und Fehlertext protokolliert. Bei hartnäckigen Zustellproblemen hilft dir der Support mit dem Protokoll weiter.

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 bezahlte Rechnungen bei Kauf auf Rechnung oder Vorkasse gibt es invoice.paid. 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

So findest du es in der App

Rechnungskit ist keine Steuerberatung und keine Rechtsberatung. Dieser Beitrag erklärt allgemeine Zusammenhänge und ersetzt weder die Beratung durch deine Steuerkanzlei (§ 5 StBerG) noch eine Rechtsberatung (§ 3 RDG). Rechnungskit richtet sich an in Deutschland ansässige Unternehmen und bereitet Belege, Steuersätze und Buchungen automatisch auf. Wie dein Einzelfall zu bewerten ist, entscheidest du, im Zweifel gemeinsam mit deiner Steuerkanzlei oder einer Anwaltskanzlei.

de en