Rezept: n8n ↔ TaskMonkey per Webhook
n8n-Workflows starten, sobald in TaskMonkey etwas passiert — und TaskMonkey-Tools aus n8n auslösen. Beide Richtungen signiert, ohne Polling.
n8n ↔ TaskMonkey per Webhook
Zwei Richtungen, beide ohne Polling:
- A — TaskMonkey → n8n: Eine neue Kundennachricht in der Inbox startet einen n8n-Workflow (Ticket im CRM anlegen, Slack-Nachricht, Google Sheet).
- B — n8n → TaskMonkey: Ein n8n-Workflow löst ein TaskMonkey-Tool aus (Lead anlegen, Bestand prüfen, Assistant eine Mail beantworten lassen).
Voraussetzungen: n8n (Cloud oder selbst gehostet, von außen per https:// erreichbar) und die Rolle Manager/Admin im Workspace. Hintergrund zu Signatur, Events und Retries: Webhooks.
A — TaskMonkey meldet sich bei n8n
1. Webhook-Node in n8n
Neuer Workflow → Node Webhook:
| Feld | Wert |
|---|---|
| HTTP Method | POST |
| Path | z. B. taskmonkey-inbox |
| Authentication | None (die Signatur prüfen wir selbst, Schritt 3) |
| Respond | Immediately |
| Options → Raw Body | an (für die Signaturprüfung) |
Die Production URL kopieren (https://n8n.example.de/webhook/taskmonkey-inbox) und den Workflow aktivieren — die Test-URL funktioniert nur, solange der Editor lauscht.
2. Endpoint in TaskMonkey
Verwaltung → Webhooks → + Ausgehend:
- URL: die Production URL aus n8n
- Events:
inbox.message.received(und bei Bedarfinbox.thread.created) - Anlegen → das Signatur-Secret (
whsec_…) kopieren, es wird nur einmal angezeigt
In n8n unter Settings → Variables (oder als Credential) TASKMONKEY_WEBHOOK_SECRET anlegen.
3. Signatur prüfen (Code-Node)
Direkt hinter den Webhook-Node einen Code-Node (JavaScript, „Run Once for All Items“):
const crypto = require('crypto');
const out = [];
for (const [i, item] of $input.all().entries()) {
const headers = item.json.headers;
const raw = (await this.helpers.getBinaryDataBuffer(i, 'data')).toString('utf8'); // Raw Body
const parts = Object.fromEntries((headers['x-taskmonkey-signature'] || '').split(',').map(p => p.split('=')));
const t = parseInt(parts.t, 10);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) throw new Error('Zeitstempel ungueltig');
const expected = crypto.createHmac('sha256', $vars.TASKMONKEY_WEBHOOK_SECRET).update(`${t}.${raw}`).digest('hex');
if (expected.length !== (parts.v1 || '').length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {
throw new Error('Signatur ungueltig');
}
out.push({ json: { delivery: headers['x-taskmonkey-delivery'], ...JSON.parse(raw) } });
}
return out;
Self-hosted n8n:
cryptomuss erlaubt sein (NODE_FUNCTION_ALLOW_BUILTIN=crypto). Ohne Raw Body (ältere n8n-Versionen) funktioniert die Prüfung nicht zuverlässig, weil n8n das JSON neu serialisiert.
4. Weiterverarbeiten
Danach normal weiter, z. B. IF auf {{$json.type}} = inbox.message.received, dann HubSpot/Slack/Sheets mit
{{$json.data.message.from.address}}— Absender{{$json.data.message.subject}}— Betreff (E-Mail){{$json.data.message.text}}— Text{{$json.data.thread.chat_id}}— Thread-ID in TaskMonkey
Doppelte Zustellungen (Retry nach Timeout) erkennst du an delivery — z. B. mit dem Node Remove Duplicates („Remove Items Processed in Previous Executions“, Feld delivery).
5. Testen
Verwaltung → Webhooks → Endpoint → Test-Event senden. In n8n erscheint eine Execution mit type: "webhook.test", in TaskMonkey steht im Zustell-Log succeeded mit Antwort 200. Schlägt es fehl, steht dort der Grund (z. B. HTTP 404 = Workflow nicht aktiv).
B — n8n löst TaskMonkey aus
1. Tool freigeben
Im Tool, das n8n auslösen soll (Builder-Chat: „gib createLead für eingehende Webhooks frei“ — der Builder fragt vorher nach):
'createLead' => [
'description' => 'Legt einen Lead an.',
'inbound_hook' => true,
'inbound_hook_write' => true, // schreibt in die Lead-Tabelle
'parameters' => [
'type' => 'object',
'properties' => [
'email' => ['type' => 'string', 'pattern' => '^[^@\s]+@[^@\s]+$'],
'name' => ['type' => 'string', 'maxLength' => 100],
'quelle' => ['type' => 'string', 'enum' => ['messe', 'website', 'telefon']],
],
'required' => ['email'],
],
'handler' => /* … */,
],
2. Hook in TaskMonkey
Verwaltung → Webhooks → + Eingehend:
- Name:
n8n Leads - Auth-Format: TaskMonkey-Signatur (oder Bearer, wenn es schnell gehen soll)
- Ziel:
createLead - Mapping leer lassen, wenn n8n die Felder direkt als
email,name,quelleschickt
URL (https://app.taskmonkey.de/api/v1/hooks/hk_…) und Secret kopieren, in n8n als Variable TM_HOOK_SECRET ablegen.
3. Signieren und senden (n8n)
Variante Bearer — ein HTTP Request-Node reicht:
| Feld | Wert |
|---|---|
| Method | POST |
| URL | die Hook-URL |
| Authentication | Generic → Header Auth: Authorization = Bearer {{$vars.TM_HOOK_SECRET}} |
| Send Body | JSON: {"email": "{{$json.email}}", "name": "{{$json.name}}", "quelle": "messe"} |
| Header | Idempotency-Key = {{$json.id}} (verhindert Doppelanlage bei Retries) |
Variante TaskMonkey-Signatur — erst ein Code-Node, der Body und Signatur baut:
const crypto = require('crypto');
const body = JSON.stringify({ email: $json.email, name: $json.name, quelle: 'messe' });
const t = Math.floor(Date.now() / 1000);
const v1 = crypto.createHmac('sha256', $vars.TM_HOOK_SECRET).update(`${t}.${body}`).digest('hex');
return { json: { body, signature: `t=${t},v1=${v1}`, delivery: `n8n-${$execution.id}-${$itemIndex}` } };
dann HTTP Request:
| Feld | Wert |
|---|---|
| Method | POST |
| URL | die Hook-URL |
| Headers | Content-Type: application/json, X-TaskMonkey-Signature: {{$json.signature}}, X-TaskMonkey-Delivery: {{$json.delivery}} |
| Body | Raw → {{$json.body}} (genau der signierte String) |
Startet der Workflow selbst durch einen TaskMonkey-Webhook (Richtung A), reiche X-TaskMonkey-Hop als Header unverändert weiter ({{$('Webhook').item.json.headers['x-taskmonkey-hop']}}) — dann erkennt TaskMonkey Schleifen auch über n8n hinweg.
Antwort: 202 {"accepted":true,"delivery_id":"…"}. Das Tool läuft Sekunden später; Ergebnis und Fehler stehen im Log des Hooks in TaskMonkey. Eine zweite Anfrage mit derselben X-TaskMonkey-Delivery bzw. Idempotency-Key wird mit 200 {"duplicate":true} quittiert und nicht erneut ausgeführt.
Assistant statt Tool
Soll ein Assistant entscheiden (z. B. „beantworte diese Anfrage aus dem Formular“), in dessen Definition 'inbound_hook' => true setzen und ihn als Ziel wählen. Er bekommt die Daten als Nachricht und arbeitet mit seinen eigenen Tools; die Antwort steht im Hook-Log. Will n8n das Ergebnis zurück, gib dem Assistant emitWebhookEvent und lass ihn am Ende z. B. custom.anfrage.beantwortet senden — das holt Richtung A wieder ab.
Stolpersteine
| Symptom | Ursache |
|---|---|
Zustell-Log: HTTP 404 |
n8n-Workflow nicht aktiviert oder Test-URL statt Production URL eingetragen |
Zustell-Log: Redirect (301) |
URL leitet um (http→https, Slash am Ende) — endgültige URL eintragen |
Zustell-Log: Ziel blockiert |
n8n läuft im internen Netz (192.168.…, localhost) — TaskMonkey sendet nur an öffentliche Adressen. n8n per Tunnel/Reverse-Proxy öffentlich machen |
| n8n: „Signatur ungueltig“ | Raw Body nicht aktiviert, oder falsches Secret (nach „Secret rotieren“ neu eintragen) |
Hook: 401 |
Body nach dem Signieren verändert (n8n-JSON-Body statt Raw), Uhr des n8n-Servers falsch (> 5 min), falsches Secret |
| Hook-Log: „Ungueltige Parameter“ | Feld fehlt oder heißt anders — Mapping setzen ({"email": "/kontakt/mail"}) oder Tool-Schema prüfen |
Hook: 508 |
Schleife erkannt: der Workflow wurde von einem TaskMonkey-Webhook gestartet, der selbst aus einem eingehenden Hook stammt (X-TaskMonkey-Hop: 2). Workflow so bauen, dass er nicht auf seinen eigenen Auslöser zurückspielt |
| Hook-Log: „nicht (mehr) freigegeben“ | inbound_hook am Tool fehlt oder ist nicht genau true |
| Endpoint „automatisch deaktiviert“ | 5 Zustellungen in Folge gescheitert — Ursache beheben, Endpoint wieder aktivieren, fehlgeschlagene Zustellungen im Log „Erneut senden“ |