{
  "id": "d4e5f6a7-b8c9-0123-def0-123456789012",
  "name": "Excel-Retter — Kontaktlisten-Bereinigung",
  "active": false,
  "versionId": "e5f6a7b8-c9d0-1234-ef01-234567890123",
  "nodes": [
    {
      "id": "webhook-trigger",
      "name": "Daten-Upload Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [200, 300],
      "webhookId": "excel-retter",
      "parameters": {
        "httpMethod": "POST",
        "path": "excel-retter",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "validate-input",
      "name": "Eingabe validieren",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [420, 300],
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\n\nconst csvData = (body.csv_data || '').trim();\nconst instructions = (body.instructions || '').trim();\nconst language = (body.language || 'de').trim();\n\nif (!csvData) {\n  return [{ json: { valid: false, error: 'Keine CSV-Daten empfangen.' } }];\n}\n\nconst lines = csvData.split('\\n').filter(l => l.trim().length > 0);\nif (lines.length < 2) {\n  return [{ json: { valid: false, error: 'Bitte mindestens eine Kopfzeile und eine Datenzeile angeben.' } }];\n}\n\nif (csvData.length > 50000) {\n  return [{ json: { valid: false, error: 'Daten zu groß. Bitte maximal 500 Zeilen auf einmal bereinigen.' } }];\n}\n\nreturn [{ json: { valid: true, csv_data: csvData, instructions, language, row_count: lines.length - 1, received_at: new Date().toISOString() } }];"
      }
    },
    {
      "id": "check-valid",
      "name": "Validierung OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [640, 300],
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" },
          "conditions": [
            {
              "leftValue": "={{ $json.valid }}",
              "rightValue": true,
              "operator": { "type": "boolean", "operation": "true" }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "error-response",
      "name": "Fehler zurückgeben",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [860, 460],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, error: $json.error }) }}",
        "options": { "responseCode": 400 }
      }
    },
    {
      "id": "build-request",
      "name": "Anfrage aufbauen",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [860, 200],
      "parameters": {
        "jsCode": "const { csv_data, instructions } = $input.first().json;\n\nconst systemPrompt = 'Du bist ein Datenbereinigungsassistent. Bereinige und normalisiere die angegebenen CSV-Daten und gib das Ergebnis IMMER als valides JSON-Objekt zurueck mit genau diesen Feldern: headers (string[]), cleaned_rows (object[]), changes (string[]), stats ({total_rows, rows_modified, duplicates_removed, issues_found}).';\n\nconst userContent = 'Bereinige und normalisiere diese CSV-Daten.\\n\\n'\n  + (instructions ? 'Spezielle Anweisungen: ' + instructions + '\\n\\n' : '')\n  + 'Folgende Bereinigungen durchfuehren:\\n'\n  + '- Fuehrende und nachfolgende Leerzeichen entfernen\\n'\n  + '- E-Mail-Adressen auf Kleinbuchstaben normalisieren und auf Gueltigkeit pruefen\\n'\n  + '- Telefonnummern in einheitliches Format bringen\\n'\n  + '- Namen korrekt kapitalisieren\\n'\n  + '- Firmennamen konsistent machen\\n'\n  + '- Exakte Duplikate und aehnliche Duplikate entfernen (behalte vollstaendigere Zeile)\\n'\n  + '\\nCSV-DATEN:\\n' + csv_data\n  + '\\n\\nGib zurueck: {\"headers\": [...], \"cleaned_rows\": [{...}], \"changes\": [\"...\"], \"stats\": {\"total_rows\": N, \"rows_modified\": N, \"duplicates_removed\": N, \"issues_found\": N}}';\n\nconst kimiRequest = {\n  model: 'kimi-k2.5',\n  max_tokens: 4096,\n  messages: [\n    { role: 'system', content: systemPrompt },\n    { role: 'user', content: userContent }\n  ]\n};\n\nreturn [{ json: { kimiRequest, received_at: $input.first().json.received_at } }];"
      }
    },
    {
      "id": "kimi-cleanup",
      "name": "Daten bereinigen (KI API)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1080, 200],
      "credentials": {
        "httpHeaderAuth": {
          "id": "kimi-api-header-auth",
          "name": "Kimi API (Moonshot)"
        }
      },
      "parameters": {
        "method": "POST",
        "url": "https://api.moonshot.ai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.kimiRequest }}",
        "options": {}
      }
    },
    {
      "id": "format-result",
      "name": "Ergebnis formatieren",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1300, 200],
      "parameters": {
        "jsCode": "const meta = $('Anfrage aufbauen').first().json;\nconst response = $input.first().json;\n\nconst content = response.choices?.[0]?.message?.content;\nif (!content) throw new Error('KI returned empty response');\n\nlet data;\ntry {\n  data = JSON.parse(content);\n} catch {\n  const match = content.match(/```(?:json)?\\s*([\\s\\S]+?)```/);\n  if (!match) throw new Error('Could not parse JSON from KI response: ' + content.substring(0, 200));\n  data = JSON.parse(match[1]);\n}\n\nconst { headers, cleaned_rows, changes, stats } = data;\nconst csvLines = [headers.join(',')];\nfor (const row of cleaned_rows) {\n  const values = headers.map(h => {\n    const val = (row[h] || '').toString();\n    return val.includes(',') || val.includes('\"') || val.includes('\\n')\n      ? '\"' + val.replace(/\"/g, '\"\"') + '\"'\n      : val;\n  });\n  csvLines.push(values.join(','));\n}\n\nreturn [{ json: { headers, cleaned_rows, changes, stats, cleaned_csv: csvLines.join('\\n'), received_at: meta.received_at } }];"
      }
    },
    {
      "id": "success-response",
      "name": "Ergebnis zurückgeben",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1520, 200],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, headers: $json.headers, cleaned_rows: $json.cleaned_rows, changes: $json.changes, stats: $json.stats, cleaned_csv: $json.cleaned_csv }) }}",
        "options": { "responseCode": 200 }
      }
    }
  ],
  "connections": {
    "Daten-Upload Webhook": {
      "main": [[{ "node": "Eingabe validieren", "type": "main", "index": 0 }]]
    },
    "Eingabe validieren": {
      "main": [[{ "node": "Validierung OK?", "type": "main", "index": 0 }]]
    },
    "Validierung OK?": {
      "main": [
        [{ "node": "Anfrage aufbauen", "type": "main", "index": 0 }],
        [{ "node": "Fehler zurückgeben", "type": "main", "index": 0 }]
      ]
    },
    "Anfrage aufbauen": {
      "main": [[{ "node": "Daten bereinigen (KI API)", "type": "main", "index": 0 }]]
    },
    "Daten bereinigen (KI API)": {
      "main": [[{ "node": "Ergebnis formatieren", "type": "main", "index": 0 }]]
    },
    "Ergebnis formatieren": {
      "main": [[{ "node": "Ergebnis zurückgeben", "type": "main", "index": 0 }]]
    }
  },
  "settings": { "executionOrder": "v1" },
  "meta": { "templateCredsSetupCompleted": true }
}
