AutomationFlowsAI & RAG › Analyze Retell AI Call Transcripts with Openai and Sync Insights to Hubspot

Analyze Retell AI Call Transcripts with Openai and Sync Insights to Hubspot

ByServify @servifylabs on n8n.io

Sales teams and agencies using Retell AI for voice outreach who want to automatically analyze every call and push insights into their CRM. Ideal for businesses running AI voice agents that need structured post-call intelligence without manual review.

Webhook trigger★★★★☆ complexityAI-powered16 nodesOpenAIHubSpotSlackGoogle Sheets
AI & RAG Trigger: Webhook Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #14331 — we link there as the canonical source.

This workflow follows the Google Sheets → OpenAI recipe pattern — see all workflows that pair these two integrations.

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": "Analyze Retell AI voice call transcripts and sync insights to HubSpot",
  "tags": [],
  "nodes": [
    {
      "id": "a1000001-0000-0000-0000-000000000001",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        -340
      ],
      "parameters": {
        "color": 6,
        "width": 540,
        "height": 480,
        "content": "## Retell AI Call Analyzer \u2192 HubSpot\n\nAutomatically analyze voice call transcripts and sync AI-powered insights to your CRM.\n\n### How it works\n1. **Webhook:** Receives the call transcript from Retell AI when a call ends.\n2. **Parse:** Extracts transcript text, caller phone number, and call metadata.\n3. **AI:** OpenAI analyzes sentiment, scores the lead 1-10, and lists action items.\n4. **CRM:** Updates the HubSpot contact record with call insights.\n5. **Route:** Hot leads trigger a Slack alert + HubSpot follow-up task. Every call is logged to Sheets.\n\n### Setup steps\n1. **Config:** Open the \"Set user config variables\" node \u2014 enter your Slack channel, score threshold, and Sheet ID.\n2. **Credentials:** Connect OpenAI, HubSpot, Slack, and Google Sheets in each node.\n3. **Retell:** Copy the webhook URL and paste it in your Retell dashboard \u2192 Webhook Settings \u2192 `call_analyzed`.\n4. **Test:** Activate the workflow, make a test call, and check that data flows through."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000001-0000-0000-0000-000000000002",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        40,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 200,
        "content": "## 1. Trigger & Config\nRetell sends a webhook when a call finishes. The config node holds your editable settings."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000001-0000-0000-0000-000000000003",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 200,
        "content": "## 2. AI Analysis\nOpenAI reads the full transcript and returns structured JSON: sentiment, lead score, action items, and a summary."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000001-0000-0000-0000-000000000004",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1100,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 200,
        "content": "## 3. CRM & Routing\nUpdates the HubSpot contact. Qualified leads trigger a Slack alert and a HubSpot follow-up task."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000001-0000-0000-0000-000000000005",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1100,
        480
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 160,
        "content": "## 4. Logging\nEvery call is appended to Google Sheets for reporting and tracking."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000001-1000-0000-0000-000000000001",
      "name": "Receive Retell call webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        80,
        300
      ],
      "parameters": {
        "path": "retell-call-analyzed",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000002",
      "name": "Set user config variables",
      "type": "n8n-nodes-base.set",
      "position": [
        300,
        300
      ],
      "parameters": {
        "mode": "manual",
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-1",
              "name": "slackChannel",
              "type": "string",
              "value": "#sales-alerts"
            },
            {
              "id": "cfg-2",
              "name": "leadScoreThreshold",
              "type": "number",
              "value": "7"
            },
            {
              "id": "cfg-3",
              "name": "googleSheetId",
              "type": "string",
              "value": "YOUR_GOOGLE_SHEET_ID_HERE"
            },
            {
              "id": "cfg-4",
              "name": "sheetName",
              "type": "string",
              "value": "Call Log"
            }
          ]
        },
        "duplicateItem": false,
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000001-1000-0000-0000-000000000003",
      "name": "Parse call transcript and metadata",
      "type": "n8n-nodes-base.code",
      "position": [
        520,
        300
      ],
      "parameters": {
        "jsCode": "// Parse Retell call_analyzed webhook payload\nconst input = $input.first().json;\nconst body = input.body || input;\n\nconst callId = body.call_id || body.call?.call_id || 'unknown';\nconst agentId = body.agent_id || body.call?.agent_id || 'unknown';\nconst fromNumber = body.from_number || body.call?.from_number || 'unknown';\nconst toNumber = body.to_number || body.call?.to_number || 'unknown';\nconst callDuration = body.call_duration_ms || body.duration_ms || 0;\nconst callStatus = body.call_status || body.status || 'completed';\nconst disconnectReason = body.disconnect_reason || 'unknown';\n\n// Extract transcript\nlet transcript = '';\nif (body.transcript) {\n  transcript = body.transcript;\n} else if (body.call?.transcript) {\n  transcript = body.call.transcript;\n} else if (Array.isArray(body.transcript_object)) {\n  transcript = body.transcript_object\n    .map(t => `${t.role}: ${t.content}`)\n    .join('\\n');\n}\n\n// Pass config variables through\nconst slackChannel = input.slackChannel || '#sales-alerts';\nconst leadScoreThreshold = input.leadScoreThreshold || 7;\nconst googleSheetId = input.googleSheetId || '';\nconst sheetName = input.sheetName || 'Call Log';\n\nreturn [{\n  json: {\n    callId,\n    agentId,\n    fromNumber,\n    toNumber,\n    callDurationSeconds: Math.round(callDuration / 1000),\n    callStatus,\n    disconnectReason,\n    transcript,\n    slackChannel,\n    leadScoreThreshold,\n    googleSheetId,\n    sheetName,\n    analyzedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000004",
      "name": "Analyze call with OpenAI",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        760,
        300
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "maxTokens": 1500,
          "temperature": 0.2
        },
        "messages": {
          "values": [
            {
              "content": "=You are a sales call analyst. Analyze this voice call transcript and return a JSON object with these fields:\n\n1. \"sentiment\": one of \"positive\", \"neutral\", \"negative\"\n2. \"leadScore\": integer 1-10 (10 = very qualified buyer)\n3. \"summary\": 2-3 sentence call summary\n4. \"actionItems\": array of strings listing follow-up actions\n5. \"keyTopics\": array of main topics discussed\n6. \"buyingSignals\": array of phrases indicating purchase intent\n7. \"objections\": array of concerns or objections raised\n8. \"recommendedNextStep\": one short sentence\n\nTranscript:\n{{ $json.transcript }}\n\nCall duration: {{ $json.callDurationSeconds }} seconds\nCall status: {{ $json.callStatus }}\n\nReturn ONLY valid JSON, no markdown formatting."
            }
          ]
        },
        "resource": "chat"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "a1000001-1000-0000-0000-000000000005",
      "name": "Extract and structure AI analysis",
      "type": "n8n-nodes-base.code",
      "position": [
        1000,
        300
      ],
      "parameters": {
        "jsCode": "// Extract and structure the AI analysis response\nconst input = $input.first().json;\nconst prev = $('Parse call transcript and metadata').first().json;\n\nlet analysis;\ntry {\n  const content = input.message?.content || input.text || input.content || JSON.stringify(input);\n  const jsonStr = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n  analysis = JSON.parse(jsonStr);\n} catch (e) {\n  analysis = {\n    sentiment: 'neutral',\n    leadScore: 5,\n    summary: 'AI analysis could not be parsed. Manual review recommended.',\n    actionItems: ['Review call recording manually'],\n    keyTopics: [],\n    buyingSignals: [],\n    objections: [],\n    recommendedNextStep: 'Follow up with prospect'\n  };\n}\n\nreturn [{\n  json: {\n    ...prev,\n    sentiment: analysis.sentiment,\n    leadScore: analysis.leadScore,\n    summary: analysis.summary,\n    actionItems: analysis.actionItems || [],\n    keyTopics: analysis.keyTopics || [],\n    buyingSignals: analysis.buyingSignals || [],\n    objections: analysis.objections || [],\n    recommendedNextStep: analysis.recommendedNextStep || 'Follow up',\n    isQualified: analysis.leadScore >= (prev.leadScoreThreshold || 7)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000006",
      "name": "Update HubSpot contact with insights",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        1280,
        200
      ],
      "parameters": {
        "resource": "contact",
        "contactId": "={{ $json.fromNumber }}",
        "operation": "update",
        "additionalFields": {
          "properties": [
            {
              "key": "last_call_summary",
              "value": "={{ $json.summary }}"
            },
            {
              "key": "lead_score",
              "value": "={{ $json.leadScore }}"
            },
            {
              "key": "last_call_sentiment",
              "value": "={{ $json.sentiment }}"
            },
            {
              "key": "last_call_date",
              "value": "={{ $json.analyzedAt }}"
            }
          ]
        }
      },
      "credentials": {
        "hubspotApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000007",
      "name": "Check if lead is qualified",
      "type": "n8n-nodes-base.if",
      "position": [
        1280,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.isQualified }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000008",
      "name": "Alert sales team about hot lead",
      "type": "n8n-nodes-base.slack",
      "position": [
        1540,
        300
      ],
      "parameters": {
        "text": "=:fire: *Hot Lead Detected!*\n\n*Phone:* {{ $('Extract and structure AI analysis').first().json.fromNumber }}\n*Lead Score:* {{ $('Extract and structure AI analysis').first().json.leadScore }}/10\n*Sentiment:* {{ $('Extract and structure AI analysis').first().json.sentiment }}\n\n*Summary:* {{ $('Extract and structure AI analysis').first().json.summary }}\n\n*Buying Signals:*\n{{ $('Extract and structure AI analysis').first().json.buyingSignals.join('\\n\u2022 ') }}\n\n*Recommended Next Step:* {{ $('Extract and structure AI analysis').first().json.recommendedNextStep }}",
        "channel": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Extract and structure AI analysis').first().json.slackChannel }}"
        },
        "resource": "message",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000009",
      "name": "Create follow-up task in HubSpot",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        1540,
        480
      ],
      "parameters": {
        "resource": "task",
        "operation": "create",
        "additionalFields": {
          "body": "={{ $('Extract and structure AI analysis').first().json.recommendedNextStep }}\n\nAction Items:\n{{ $('Extract and structure AI analysis').first().json.actionItems.join('\\n- ') }}",
          "status": "NOT_STARTED",
          "subject": "=Follow up with hot lead: {{ $('Extract and structure AI analysis').first().json.fromNumber }}",
          "priority": "HIGH"
        }
      },
      "credentials": {
        "hubspotApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000001-1000-0000-0000-000000000010",
      "name": "Log call analysis to tracking sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1280,
        600
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $('Extract and structure AI analysis').first().json.analyzedAt }}",
            "Call ID": "={{ $('Extract and structure AI analysis').first().json.callId }}",
            "Summary": "={{ $('Extract and structure AI analysis').first().json.summary }}",
            "Qualified": "={{ $('Extract and structure AI analysis').first().json.isQualified }}",
            "Sentiment": "={{ $('Extract and structure AI analysis').first().json.sentiment }}",
            "Lead Score": "={{ $('Extract and structure AI analysis').first().json.leadScore }}",
            "From Number": "={{ $('Extract and structure AI analysis').first().json.fromNumber }}",
            "Action Items": "={{ $('Extract and structure AI analysis').first().json.actionItems.join('; ') }}",
            "Duration (sec)": "={{ $('Extract and structure AI analysis').first().json.callDurationSeconds }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Extract and structure AI analysis').first().json.sheetName }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Extract and structure AI analysis').first().json.googleSheetId }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "a1000001-1000-0000-0000-000000000011",
      "name": "Respond to Retell webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1540,
        140
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"processed\", \"callId\": \"{{ $('Parse call transcript and metadata').first().json.callId }}\"}"
      },
      "typeVersion": 1.1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "connections": {
    "Analyze call with OpenAI": {
      "main": [
        [
          {
            "node": "Extract and structure AI analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set user config variables": {
      "main": [
        [
          {
            "node": "Parse call transcript and metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if lead is qualified": {
      "main": [
        [
          {
            "node": "Alert sales team about hot lead",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create follow-up task in HubSpot",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Receive Retell call webhook": {
      "main": [
        [
          {
            "node": "Set user config variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract and structure AI analysis": {
      "main": [
        [
          {
            "node": "Update HubSpot contact with insights",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check if lead is qualified",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log call analysis to tracking sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse call transcript and metadata": {
      "main": [
        [
          {
            "node": "Analyze call with OpenAI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update HubSpot contact with insights": {
      "main": [
        [
          {
            "node": "Respond to Retell webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

Sales teams and agencies using Retell AI for voice outreach who want to automatically analyze every call and push insights into their CRM. Ideal for businesses running AI voice agents that need structured post-call intelligence without manual review.

Source: https://n8n.io/workflows/14331/ — 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

Automatically detects missed Zoom demos booked via Calendly and triggers AI-powered follow-up sequences.

HTTP Request, OpenAI, Email Send +3
AI & RAG

Imagine your recruitment process transformed into a sleek, efficient, AI-powered assembly line for talent. That's exactly what this system creates. It automates the heavy lifting, allowing your human

Google Sheets, OpenAI, Gmail +2
AI & RAG

Transform customer feedback into actionable insights automatically with AI analysis, professional PDF reports, personalized emails, and real-time team notifications. Overview Features Demo Prerequisit

OpenAI, Gmail, Google Sheets +2
AI & RAG

Transform your webinar registrations from basic form submissions into a verified, personalized, and premium attendee experience.

N8N Nodes Verifiemail, Slack, Stop And Error +4
AI & RAG

This workflow automates the initial screening process for new job applications, freeing up your recruitment team to focus on qualified candidates. It receives applications from a webhook, uses OpenAI

HTTP Request, OpenAI, Google Sheets +2