n8n WhatsApp KI-Agent: Produktions-Setup Schritt für Schritt

So baust du einen n8n WhatsApp KI-Agenten für die Produktion: signierter Webhook, sofortiges 200, Duplikatschutz, AI Agent mit Memory, Retries und Übergabe an Menschen.

Ein Graph aus abgerundeten Blöcken, die durch Leitungen verbunden sind; der aktive Pfad leuchtet grün und endet in einem Smartphone.

Das Wichtigste

  • Ein n8n WhatsApp KI-Agent braucht nur einen Webhook-Node, einen Code-Node, der die Signatur prüft, einen AI-Agent-Node mit Memory und einen HTTP-Request-Node, der die Antwort sendet.
  • Schalte beim Webhook-Node die Option Raw Body ein, denn die Signatur deckt genau die Bytes ab, die whasp gesendet hat, und ein neu serialisierter JSON-Body kann die Prüfung nicht bestehen.
  • Antworte direkt nach der Prüfung mit 200, entferne Duplikate mit einem Remove-Duplicates-Node auf Basis der Event-ID und sende Antworten mit einem Idempotency-Key, damit ein Retry nie doppelt sendet.
  • Nimm in der Produktion Postgres Chat Memory oder ein anderes datenbankgestütztes Memory, denn Simple Memory liegt im Arbeitsspeicher des n8n-Prozesses und vergisst einen Chat nach einer Stunde Stille.
  • Die Übergabe an einen Menschen liegt in n8n, nicht in whasp: Führe pro Chat ein Flag in einer Data table und prüfe es vor jedem KI-Aufruf.

Um einen n8n WhatsApp KI-Agenten zu bauen, richtest du einen Webhook-Node auf die eingehenden Events des Agenten, prüfst die Signatur in einem Code-Node, antwortest sofort mit 200, verwirfst doppelte Events, lässt einen AI-Agent-Node mit Chatmodell und Memory laufen (Schlüssel ist die Telefonnummer des Absenders) und sendest die Antwort mit einem HTTP-Request-Node. Diese Anleitung nutzt whasp für die WhatsApp-Seite und lief auf n8n 2.43.8.

Warum nicht den WhatsApp-Node von n8n nutzen?

Der eingebaute WhatsApp-Business-Cloud-Node von n8n sendet Nachrichten, Templates und Medien über Metas offizielle WhatsApp Business Platform. Seit dem 15. Januar 2026 schließen die Bedingungen dieser Plattform allgemeine KI-Assistenten aus, wie WhatsApp KI-Chatbot-Verbot 2026 erklärt, und die anderen Optionen bringen jeweils eigene Abwägungen mit. Diese Anleitung nutzt eine Nummer, die whasp betreibt. n8n braucht dafür keinen besonderen Node: whasp sendet Events an eine URL und nimmt Antworten über eine einfache HTTPS-API entgegen, der Webhook- und der HTTP-Request-Node genügen also.

Die Kette hat acht Schritte: Webhook, Verify signature (Code), Respond to Webhook, Text message? (If), Remove Duplicates, Is STOP? (If), AI Agent mit seinem Modell und Memory, Send reply (HTTP Request). Wir haben am 19. Oktober 2026 alles außer dem Postgres-Node auf n8n 2.43.8 ausgeführt, mit gemocktem Sprachmodell und gemockter whasp API.

Dein n8n muss über öffentliches HTTPS erreichbar sein (hinter einem Reverse Proxy setzt du N8N_WEBHOOK_URL). Dazu brauchst du eine whasp-Nummer, einen API-Key und ein Modell-Credential.

Wie empfängst du whasp-Events in n8n?

Füge einen Webhook-Node hinzu und stelle ein:

  • HTTP Method: POST. Path: whasp.
  • Respond: Using ‘Respond to Webhook’ Node.
  • Options, Raw Body: an.

Der Webhook-Node hat eine Test-URL und eine Produktions-URL. Die Test-URL (/webhook-test/whasp) funktioniert, solange du auf Listen for Test Event klickst. Die Produktions-URL (/webhook/whasp) funktioniert erst, nachdem du den Workflow veröffentlicht hast. Registriere die Produktions-URL bei whasp. Die Antwort enthält das Signing Secret (whsec_...), das du als WHASP_WEBHOOK_SECRET speicherst:

curl -X PUT https://api.whasp.ai/v1/webhook \
  -H "Authorization: Bearer $WHASP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://n8n.example.com/webhook/whasp"}'

Raw Body ist wichtig. whasp signiert genau die Bytes, die es sendet, nach dem Schema von Standard Webhooks. Mit eingeschalteter Option behält der Node diese Bytes in binary.data neben dem geparsten body. Wenn du das geparste JSON neu serialisierst, können sich Leerraum oder Schlüsselreihenfolge ändern, und die Prüfung schlägt fehl.

Rufe POST /v1/webhook/test auf, um ein webhook.test-Event zu senden und zu beobachten, wie es ankommt.

Wie prüfst du die Signatur in einem Code-Node?

Füge einen Code-Node mit dem Namen Verify signature hinzu, Mode Run Once for All Items, Sprache JavaScript:

const crypto = require('crypto');

if (!$env.WHASP_WEBHOOK_SECRET) throw new Error('Set WHASP_WEBHOOK_SECRET (the whsec_ secret from whasp)');

const headers = $input.first().json.headers;
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];

// Genau die Bytes, die whasp signiert hat: Die Option Raw Body des Webhook-Nodes hält sie in binary.data.
const raw = await this.helpers.getBinaryDataBuffer(0, 'data');

const key = Buffer.from($env.WHASP_WEBHOOK_SECRET.replace(/^whsec_/, ''), 'base64');
const expected = crypto.createHmac('sha256', key)
  .update(`${id}.${timestamp}.`)
  .update(raw)
  .digest();

const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300; // 5 Minuten
const signed = String(headers['webhook-signature'] ?? '').split(' ').some((part) => {
  const [version, sig] = part.split(',');
  if (version !== 'v1' || !sig) return false;
  const given = Buffer.from(sig, 'base64');
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
});

if (!id || !timestamp || !fresh || !signed) return [{ json: { valid: false } }];

const event = JSON.parse(raw.toString('utf8'));
const m = event.data?.message ?? {};
return [{
  json: {
    valid: true,
    eventId: id,
    type: event.type,
    number: m.number,   // die whasp-Nummer, die die Nachricht empfangen hat
    chat: m.chat,       // das Telefon des Absenders, E.164
    name: m.sender_name ?? null,
    text: m.text ?? '',
  },
}];

Der Code folgt der Regel von whasp: HMAC-SHA256 über <id>.<timestamp>.<raw body>, mit dem base64-dekodierten Teil des Secrets nach whsec_ als Schlüssel und einem Zeitstempel, der höchstens fünf Minuten alt ist. Er gibt valid: false zurück, statt einen Fehler zu werfen. Ein Fehler vor dem Respond-to-Webhook-Node lässt n8n mit 500 antworten, und whasp würde das Event bis zu 12-mal wiederholen. Ein sauberes 401 ist besser.

Zwei Einstellungen bringen bei selbst gehostetem n8n 2.x viele ins Stolpern, und beide scheitern mit einer klaren Meldung. Seit n8n 2.0 blockiert der Code-Node Umgebungsvariablen standardmäßig, und ohne N8N_BLOCK_ENV_ACCESS_IN_NODE=false scheitert dieser Node mit „access to env vars denied“. Auch eingebaute Module sind blockiert: Ohne NODE_FUNCTION_ALLOW_BUILTIN=crypto erscheint „Module ‘crypto’ is disallowed“. Bei externen Task Runnern gehört die zweite Variable in die eigene Konfiguration des Runners. In der n8n Cloud ist crypto erlaubt. Wenn du keine Umgebungsvariablen setzen kannst, lege dort, wo dein Tarif sie bietet, eine Custom Variable an und lies stattdessen $vars.WHASP_WEBHOOK_SECRET.

Wie antwortest du schnell und überspringst Duplikate?

whasp wertet jede 2xx-Antwort innerhalb von 10 Sekunden als zugestellt. Andernfalls wiederholt es nach 30 Sekunden, 2 Minuten, 10 Minuten, 30 Minuten und dann stündlich, bis zu 12-mal. Events können doppelt und in falscher Reihenfolge ankommen. Drei Nodes kümmern sich darum.

  1. Respond to Webhook. Respond With: No Data. Response Code: {{ $json.valid ? 200 : 401 }}. Der Node reicht seine Eingabe nach der Antwort weiter, der Workflow läuft also weiter, während whasp seine Antwort schon hat.
  2. Text message? (If). Mach nur weiter, wenn valid true ist, type gleich message.received ist und text nicht leer ist. Alles andere (message.sent, number.status_changed, ein Foto ohne Text) fällt in den false-Zweig. Verbinde dort message.failed und number.status_changed mit einem Alarm.
  3. Remove Duplicates. Operation: Remove Items Processed in Previous Executions. Keep Items Where: Value Is New. Value to Dedupe On: {{ $json.eventId }}. Die Standardhistorie umfasst 10.000 Einträge.

Wir haben dasselbe signierte Event zweimal und ein manipuliertes einmal gesendet: Das erste bekam eine Antwort, das Duplikat bekam ein 200 und keinen zweiten Lauf, das manipulierte bekam ein 401. Eine Grenze gibt es: Hast du einmal mit 200 geantwortet, geht das Event bei einem Absturz in n8n verloren, weil whasp es nicht erneut sendet. Wenn dir das wichtig ist, schreibe jedes Event vor der Bestätigung in eine Tabelle oder Queue. WhatsApp-Webhooks verarbeiten: Signatur, Retries, Duplikate behandelt das Muster.

Wie richtest du AI Agent, Memory und Antwort ein?

AI Agent. Source for Prompt (User Message): Define below. Prompt (User Message): {{ $json.text }}. Schreibe unter Options, System Message, wer der Agent ist und wie er schreiben soll, zum Beispiel „Du bist Ava, eine freundliche Assistentin, die auf WhatsApp schreibt. Nur Klartext, kein Markdown. Stell immer nur eine Frage auf einmal.“ Für einen ausführlicheren Prompt siehe KI-Agent auf WhatsApp wie ein Mensch schreiben lassen. Die Antwort des Agenten kommt in $json.output an.

Chat Model. Hänge ein OpenAI Chat Model oder ein Anthropic Chat Model an. Beide laden die Modellliste aus deinem Account, wähle dort also ein aktuelles Modell.

Memory. Hänge Postgres Chat Memory an. Setze Session ID auf Define below und Key auf {{ $json.number }}:{{ $json.chat }}, sodass jede Person pro Nummer einen eigenen Verlauf hat. Die Standardtabelle heißt n8n_chat_histories, und Context Window Length steht standardmäßig auf 5, erhöhe den Wert also so weit, wie dein Prompt-Budget es erlaubt. Vermeide Simple Memory in der Produktion. Sein Quellcode hält Sessions in einer Map im n8n-Prozess und löscht jede Session, die eine Stunde ungenutzt bleibt, und seine Doku sagt, dass er im Queue Mode nicht funktioniert. Die Server von whasp speichern keine Nachrichten, der Verlauf gehört also dir, und eine Datenbank, die du selbst kontrollierst, ist das richtige Zuhause dafür. WhatsApp-KI-Agent mit Konversationsgedächtnis: Verlauf, Fakten geht tiefer.

Send reply. Füge einen HTTP-Request-Node hinzu:

Feld Wert
Method, URL POST, https://api.whasp.ai/v1/messages
Authentication Generic Credential Type, Header Auth: Name Authorization, Value Bearer wsk_...
Send Headers Idempotency-Key: {{ $('Verify signature').first().json.eventId }}
Send Body, JSON, Using JSON {{ { to: $('Verify signature').first().json.chat, text: $json.output } }}
Settings Retry On Fail an, Max Tries 3, Wait Between Tries 2000 ms

Baue den Body als Ausdruck, der ein Objekt zurückgibt. Wenn du Modelltext in einen JSON-String klebst, bricht das beim ersten Anführungszeichen oder Zeilenumbruch. Der Idempotency-Key macht Wiederholungen sicher: Derselbe Key liefert die erste Nachricht zurück und sendet nie doppelt. n8n erlaubt höchstens 5 Versuche mit höchstens 5 Sekunden dazwischen, es deckt also kurze Aussetzer ab, keine Ausfälle. whasp antwortet mit 202 und sendet die Nachrichten einer Nummer der Reihe nach, im Abstand von etwa einer Sekunde.

Wie gehst du mit STOP, Fehlern und Nachrichtenschüben um?

whasp meldet einen Chat selbst ab, wenn die Person STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, QUIT oder irgendeine Schreibweise von „opt out“ sendet, und spätere Sendungen an diesen Chat liefern 403 chat_opted_out. Die Nachricht erreicht trotzdem deinen Webhook, also füge nach Remove Duplicates einen If-Node namens Is STOP? hinzu. Setze den Wert auf den Ausdruck {{ /^(STOP|UNSUBSCRIBE|OPTOUT|CANCEL|END|QUIT)$/.test($json.text) || /^opt[\s_-]*out$/i.test($json.text) }} und die Bedingung auf Boolean, is true. Im true-Zweig überspringst du das Modell und sendest einen einzelnen HTTP Request mit "override_optout": true und einer Zeile wie „Du hast dich abgemeldet. Schreib jederzeit wieder, wenn du weiterreden möchtest.“ Höfliche Bitten wie „bitte hör auf“ musst du selbst abfangen.

Für Fehler öffnest du Workflow Settings, setzt einen Error workflow und baust ihn aus einem Error-Trigger-Node, der in Slack oder per E-Mail meldet. Achte auf die 429-Antworten: rate_limited heißt mehr als 100 API-Anfragen in 10 Sekunden pro Account, und number_daily_limit heißt, dass die Nummer 1.500 Nachrichten in 24 Stunden gesendet hat. Ein 409 number_unavailable heißt, dass die Nummer gesperrt, offline oder stillgelegt ist.

Nachrichtenschübe verlangen eine Entscheidung. Eine Person, die in fünf Sekunden drei Nachrichten schickt, startet drei Läufe und bekommt drei Antworten. Entwickler im n8n-Forum empfehlen, schnell aufeinanderfolgende Nachrichten eines Kunden per Debouncing zu einem Turn zusammenzufassen, bevor das Modell aufgerufen wird. Diesen Teil haben wir nicht gebaut.

Wie übergibst du einen Chat an einen Menschen?

whasp ist eine Leitung: Es hat keinen Posteingang und keine Übernahme, der Zustand der Übergabe liegt also in n8n. Ein brauchbarer Entwurf nutzt eine Data table namens chat_state mit den Spalten chat und handoff. Diesen Teil haben wir nicht ausgeführt, aber die Operationen der Nodes sind dokumentiert.

  1. Füge nach Remove Duplicates einen Data-table-Node hinzu: Row, If Row Exists, mit chat gleich {{ $json.chat }} und handoff true. Der true-Zweig leitet die Nachricht an Slack oder dein Helpdesk weiter und endet. Der false-Zweig geht weiter zum Agenten.
  2. Setze das Flag mit einem Data-table-Upsert-row, wenn der Text zu einer Regel wie /mensch|echte person/i passt oder wenn dein Agent die Übergabe verlangt. Sende dann „Bald antwortet dir hier ein Mensch.“
  3. Baue einen zweiten Workflow, der auslöst, wenn Mitarbeiter antworten, POST /v1/messages mit demselben Chat aufruft und handoff bei einem Fortsetzungsbefehl per Upsert wieder auf false setzt.

Prüfe das Flag direkt vor dem Senden der Modellantwort noch einmal, denn eine Übergabe kann beginnen, während das Modell noch denkt.

Was du als Nächstes tun kannst

Veröffentliche den Workflow, registriere seine Produktions-URL mit PUT /v1/webhook, sende POST /v1/webhook/test und schreibe dann von deinem eigenen Smartphone an deine Nummer. Kommt nichts an, zeigt GET /v1/webhook/deliveries jeden Versuch mit Status. Der Quickstart führt durch die API. Für denselben Ablauf in Code siehe ChatGPT mit WhatsApp verbinden: KI-Agent mit OpenAI API bauen.

Fragen

Wie verbinde ich n8n mit WhatsApp für einen KI-Agenten?

Empfange eingehende Nachrichten mit einem Webhook-Node, lass einen AI-Agent-Node laufen und sende die Antwort mit einem HTTP-Request-Node, der POST https://api.whasp.ai/v1/messages aufruft. Der eigene WhatsApp-Business-Cloud-Node von n8n nutzt Metas offizielle API, die für einen allgemeinen Assistenten nicht passt.

Warum meldet mein n8n-Code-Node, dass das Modul crypto nicht erlaubt ist?

Seit n8n 2.0 läuft der Code-Node auf Task Runnern und blockiert eingebaute Module standardmäßig. Setze bei selbst gehostetem n8n NODE_FUNCTION_ALLOW_BUILTIN=crypto dort, wo der Task Runner seine Umgebung liest. Die n8n Cloud erlaubt crypto bereits.

Warum meldet mein n8n-Code-Node access to env vars denied?

n8n 2.0 blockiert Umgebungsvariablen im Code-Node standardmäßig. Setze bei selbst gehostetem n8n N8N_BLOCK_ENV_ACCESS_IN_NODE=false oder speichere das Secret als Custom Variable und lies es mit $vars.

Wie verhindere ich, dass n8n dieselbe WhatsApp-Nachricht zweimal beantwortet?

Setze nach dem Webhook einen Remove-Duplicates-Node ein, der Einträge aus früheren Ausführungen entfernt, und dedupliziere über die Event-ID aus dem Header webhook-id. Sende dann die Antwort mit derselben ID als Idempotency-Key.

Welchen n8n-Memory-Node sollte ein WhatsApp-Agent nutzen?

Nimm Postgres Chat Memory oder Redis Chat Memory, mit der Telefonnummer des Absenders als Session-Key. Simple Memory läuft im Prozess, wird nach einer Stunde ohne Nutzung verworfen und funktioniert im Queue Mode nicht.

Alle Artikel