AutomationFlowsAI & RAG › Call Center Improvement Engine

Call Center Improvement Engine

Call Center Improvement Engine. Uses httpRequest. Webhook trigger; 6 nodes.

Webhook trigger★★★★☆ complexity6 nodesHTTP Request
AI & RAG Trigger: Webhook Nodes: 6 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Call Center Improvement Engine",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "improvement",
        "responseMode": "lastNode",
        "options": {}
      },
      "id": "a1b2c3d4-0001-0001-0001-a1b2c3d40001",
      "name": "Improvement Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        200,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// \u2500\u2500 Step 1: Build the failure-analysis prompt \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// NOTE: n8n is the required improvement orchestration path for this repo.\n// This prompt must produce output that passes the same validation rules\n// as the local ImprovementEngine in agent/improvement_engine.py.\nconst body = $input.first().json.body;\nconst transcripts = body.transcripts.join('\\n\\n---\\n\\n');\nconst metrics = body.metrics;\nconst script = body.current_script;\n\n// Derive allowed keys from the actual script \u2014 never hard-code handler names.\n// This ensures the constraint stays correct when handlers are added or renamed.\nconst allowedKeys = Object.keys(script.objection_handlers || {}).join(' OR ');\n\nconst analysisPrompt = `You are a senior sales coach analyzing failed sales call transcripts.\\n\\nTRANSCRIPTS:\\n${transcripts}\\n\\nAGGREGATE METRICS:\\n- Success rate: ${(metrics.success_rate * 100).toFixed(0)}%\\n- Calls analyzed: ${metrics.calls_analyzed}\\n- Common failure: ${metrics.common_failure}\\n- Avg score: ${(metrics.avg_score * 100).toFixed(0)}%\\n\\nIdentify the single most impactful script weakness that caused these failures.\\n\\nCRITICAL: \"failed_section\" MUST be one of these exact keys from the current script:\\n${allowedKeys}\\nDo NOT invent new handler names. Do NOT use values like \"close\", \"pitch\", or any key not in the list above.\\nPick the closest existing key from the list.\\n\\nRespond ONLY with valid JSON (no markdown, no extra text):\\n{\\n  \"failure_moment\": \"brief description of exact turn where call was lost\",\\n  \"root_cause\": \"one-sentence root cause\",\\n  \"failed_section\": \"one of: ${allowedKeys}\",\\n  \"specific_weakness\": \"what the agent said that failed\",\\n  \"improvement_direction\": \"concrete direction for improvement (include: ROI example, risk reversal, urgency hook)\",\\n  \"confidence\": \"high OR medium OR low\"\\n}`;\n\nreturn [{\n  json: {\n    api_key: body.api_key || $env.ANTHROPIC_API_KEY || '',\n    model: 'claude-haiku-4-5-20251001',\n    current_script: script,\n    metrics: metrics,\n    analysis_request: {\n      model: 'claude-haiku-4-5-20251001',\n      max_tokens: 600,\n      system: 'You are a sales training expert. Return only valid JSON.',\n      messages: [{ role: 'user', content: analysisPrompt }]\n    }\n  }\n}];"
      },
      "id": "a1b2c3d4-0002-0002-0002-a1b2c3d40002",
      "name": "Build Analysis Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $json.api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json.analysis_request) }}",
        "options": {}
      },
      "id": "a1b2c3d4-0003-0003-0003-a1b2c3d40003",
      "name": "LLM: Analyze Failures",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// \u2500\u2500 Step 2: Parse FailureAnalysis + build rewrite prompt \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// NOTE: n8n is the required improvement orchestration path for this repo.\n// The rewrite prompt uses the same structured handler format as ImprovementEngine.\nconst prev = $input.first().json;\n\n// Parse the analysis JSON from the LLM response\nlet analysis;\ntry {\n  let rawText = prev.content[0].text.trim();\n  rawText = rawText.replace(/^```(?:json)?\\s*/m, '').replace(/\\s*```$/m, '');\n  analysis = JSON.parse(rawText);\n} catch(e) {\n  // Fallback if parsing fails \u2014 Python client will validate failed_section\n  analysis = {\n    failure_moment: 'objection not handled',\n    root_cause: 'Weak objection handler \u2014 no value proof or risk reversal',\n    failed_section: 'price',\n    specific_weakness: 'Acknowledged concern but offered no counter-evidence',\n    improvement_direction: 'Add concrete ROI proof point, 30-day free trial offer, urgency hook',\n    confidence: 'medium'\n  };\n}\n\n// Retrieve current_script passed through the chain\nconst currentScript = $('Build Analysis Prompt').first().json.current_script;\nconst apiKey = $('Build Analysis Prompt').first().json.api_key;\nconst section = analysis.failed_section;\nconst nextVersion = (currentScript.version || 1) + 1;\nconst currentHandler = (currentScript.objection_handlers || {})[section] || {};\nconst currentHandlerJson = JSON.stringify(currentHandler, null, 2);\n\nconst rewritePrompt = `You are a sales script optimization expert.\\n\\nCURRENT SCRIPT (v${currentScript.version}):\\n${JSON.stringify(currentScript, null, 2)}\\n\\nFAILURE ANALYSIS:\\n- Weak section: objection_handlers[\"${section}\"]\\n- Current handler: ${currentHandlerJson}\\n- Root cause: ${analysis.root_cause}\\n- Improvement direction: ${analysis.improvement_direction}\\n\\nTASK: Rewrite ONLY the \"${section}\" objection handler. Keep ALL other fields identical \u2014 do not rewrite the script wholesale.\\n\\nThe handler MUST be a JSON object with exactly these three keys:\\n  \"proof_point\"   \u2014 a concrete, data-backed statement (specific ROI figure, savings amount,\\n                    adoption stat, or relevant proof). Be specific \u2014 no vague claims.\\n  \"risk_reversal\" \u2014 removes the perceived risk of saying yes (free trial, no contract,\\n                    cancel anytime, start free, etc.)\\n  \"urgency\"       \u2014 a soft time-based or opportunity-based hook ending with a question\\n\\nReturn the COMPLETE updated script as valid JSON only (no markdown, no extra text):\\n- \"version\": ${nextVersion}\\n- All original sections preserved exactly, only the \"${section}\" handler updated\\n- \"metadata.parent_version\": ${currentScript.version}\\n- \"metadata.improvement_rationale\": one sentence explaining what specific fact was added and why\\n\\nThis JSON will be validated by the Python client (agent/n8n_client.py) against the same\\nrules as the local ImprovementEngine. Return only valid JSON.`;\n\nreturn [{\n  json: {\n    api_key: apiKey,\n    analysis: analysis,\n    current_script: currentScript,\n    rewrite_request: {\n      model: 'claude-haiku-4-5-20251001',\n      max_tokens: 1500,\n      system: 'You are a sales script optimization expert. Return only valid JSON.',\n      messages: [{ role: 'user', content: rewritePrompt }]\n    }\n  }\n}];"
      },
      "id": "a1b2c3d4-0004-0004-0004-a1b2c3d40004",
      "name": "Parse Analysis + Build Rewrite",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        920,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $json.api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json.rewrite_request) }}",
        "options": {}
      },
      "id": "a1b2c3d4-0005-0005-0005-a1b2c3d40005",
      "name": "LLM: Rewrite Script",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1160,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// \u2500\u2500 Step 3: Parse new script + package response \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// NOTE: n8n is the required improvement orchestration path for this repo.\n// We apply the same version-pinning and key-merge rules as ImprovementEngine\n// here so the Python client receives a consistent, validated payload.\nconst llmResponse = $input.first().json;\nconst analysis = $('Parse Analysis + Build Rewrite').first().json.analysis;\nconst currentScript = $('Parse Analysis + Build Rewrite').first().json.current_script;\n\nlet newScript;\ntry {\n  let rawText = llmResponse.content[0].text.trim();\n  rawText = rawText.replace(/^```(?:json)?\\s*/m, '').replace(/\\s*```$/m, '');\n  newScript = JSON.parse(rawText);\n} catch(e) {\n  // Return structured error so Python client raises a clear RuntimeError\n  return [{ json: { error: 'Failed to parse new script: ' + e.message, raw: llmResponse.content[0].text } }];\n}\n\n// Enforce version \u2014 never trust the LLM to increment correctly\nconst nextVersion = (currentScript.version || 1) + 1;\nnewScript.version = nextVersion;\n\n// Ensure metadata exists with required fields\nif (!newScript.metadata) newScript.metadata = {};\nnewScript.metadata.created_at = new Date().toISOString();\nif (!newScript.metadata.parent_version) {\n  newScript.metadata.parent_version = currentScript.version;\n}\n\n// Preserve any top-level keys the LLM dropped (greeting, pitch, close, name, etc.)\nfor (const key of Object.keys(currentScript)) {\n  if (!(key in newScript)) {\n    newScript[key] = currentScript[key];\n  }\n}\n\nreturn [{\n  json: {\n    new_script: newScript,\n    analysis: analysis\n  }\n}];"
      },
      "id": "a1b2c3d4-0006-0006-0006-a1b2c3d40006",
      "name": "Parse New Script",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1400,
        300
      ]
    }
  ],
  "connections": {
    "Improvement Webhook": {
      "main": [
        [
          {
            "node": "Build Analysis Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Analysis Prompt": {
      "main": [
        [
          {
            "node": "LLM: Analyze Failures",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM: Analyze Failures": {
      "main": [
        [
          {
            "node": "Parse Analysis + Build Rewrite",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Analysis + Build Rewrite": {
      "main": [
        [
          {
            "node": "LLM: Rewrite Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM: Rewrite Script": {
      "main": [
        [
          {
            "node": "Parse New Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": true,
    "description": "Required improvement orchestration path. Receives failed call transcripts from Python, runs a 2-step LLM pipeline (analyze failures \u2192 rewrite weak script section), returns the improved script. The response is validated by agent/n8n_client.py against the same rules as the local ImprovementEngine. Import this workflow, toggle it ON, then run: python3 main.py --n8n"
  },
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Call Center Improvement Engine. Uses httpRequest. Webhook trigger; 6 nodes.

Source: https://github.com/purva-b/Self-Improving-Call-Center-Agent/blob/aed32f468a8092109ece0ab1c051d90570636813/n8n/workflow_improvement.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Jigsaw API key for image processing, I use this as a gatekeeper/second pair of eyes. LINK to their website https://jigsawstack.com/ SECOND A postgress DATABASE (I use Supabase) LlamaCloud for the pars

HTTP Request, Postgres, Stop And Error +2
AI & RAG

Onsite Photos to Jobs (SMS Agent). Uses dataTable, twilio, httpRequest, airtable. Webhook trigger; 62 nodes.

Data Table, Twilio, HTTP Request +1
AI & RAG

AML Alert Triage. Uses httpRequest. Webhook trigger; 57 nodes.

HTTP Request
AI & RAG

W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 50 nodes.

Postgres, Redis, HTTP Request
AI & RAG

W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 48 nodes.

Postgres, Redis, HTTP Request