AutomationFlowsAI & RAG › Extract Sales Insights From Scoot Call Transcripts Using Gemini

Extract Sales Insights From Scoot Call Transcripts Using Gemini

ByAI Sales Agent HQ @daniellopezscoot on n8n.io

Automatically extract sales insights from call transcripts and update your CRM.

Webhook trigger★★★★☆ complexityAI-powered23 nodesHTTP RequestAgentGoogle Gemini ChatGmail
AI & RAG Trigger: Webhook Nodes: 23 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Gmail 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
{
  "id": "UlWMRaijIFqktxa3tEzfM",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Extract sales insights from call transcripts with Scoot and Gemini",
  "tags": [
    {
      "id": "K6D3K6nP6k9TByzr",
      "name": "sales-automation",
      "createdAt": "2026-01-14T17:39:47.009Z",
      "updatedAt": "2026-01-14T17:39:47.009Z"
    },
    {
      "id": "H2JohKUHZtmiDbz3",
      "name": "call-transcription",
      "createdAt": "2026-01-15T16:32:28.124Z",
      "updatedAt": "2026-01-15T16:32:28.124Z"
    },
    {
      "id": "rCMRdwommxykeD6w",
      "name": "scoot-integration",
      "createdAt": "2026-01-15T16:32:28.114Z",
      "updatedAt": "2026-01-15T16:32:28.114Z"
    }
  ],
  "nodes": [
    {
      "id": "f130050e-7bb0-41ea-a715-bf921603d50f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4064,
        1968
      ],
      "parameters": {
        "color": 5,
        "width": 360,
        "height": 380,
        "content": "## How it works\n\nThis workflow automatically extracts structured sales data from call transcripts:\n\n1. **Trigger** \u2192 Scoot sends webhook when transcription completes\n2. **Fetch** \u2192 Retrieves full transcript from Scoot API\n3. **Extract** \u2192 Gemini AI pulls out budget, competitors, objections, next steps\n4. **Update** \u2192 Writes extracted fields to your CRM\n5. **Notify** \u2192 Emails formatted summary to the sales rep\n\n## Setup steps\n\n1. **Scoot API** \u2192 Create Header Auth credential with your API key\n2. **Gemini** \u2192 Add Google AI API key from aistudio.google.com\n3. **Gmail** \u2192 Connect OAuth2 for sending notifications\n4. **Webhook** \u2192 Copy URL to Scoot dashboard \u2192 Webhooks\n5. **CRM** \u2192 Replace mock node with HubSpot/Salesforce/Pipedrive\n6. **Test** \u2192 Click \"Test with sample data\" to verify setup"
      },
      "typeVersion": 1
    },
    {
      "id": "bc84fcf9-f129-4442-8686-72fb77568ca2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4480,
        1872
      ],
      "parameters": {
        "color": 7,
        "height": 112,
        "content": "**Trigger options**\nWebhook receives Scoot notifications. Manual trigger uses sample data for testing without real calls."
      },
      "typeVersion": 1
    },
    {
      "id": "24bb1a96-9c73-437d-8706-00d52e993b29",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4944,
        1920
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 96,
        "content": "**Data preparation**\nFetches transcript from Scoot, checks if processing is complete, handles retries for incomplete transcripts."
      },
      "typeVersion": 1
    },
    {
      "id": "950e5fcc-08bb-40e6-b4a5-5b1395cf7805",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5584,
        1760
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 128,
        "content": "**AI extraction**\nAI Agent with Gemini model extracts: budget, competitors, objections, timeline, decision maker, next steps, pain points, buying signals."
      },
      "typeVersion": 1
    },
    {
      "id": "0b7fb807-a07a-4b1b-a897-183aec9f5fc2",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6048,
        1856
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 112,
        "content": "**Output**\nReplace mock CRM node with your integration. Gmail sends formatted summary to the rep who made the call."
      },
      "typeVersion": 1
    },
    {
      "id": "b10597fc-3736-4e8d-baa2-e84a18c8aa90",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5648,
        2448
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 112,
        "content": "**Retry logic**\nIf transcript still processing: waits 1 hour, retries up to 6 times. Adjust wait time in \"Wait 1 hour\" node."
      },
      "typeVersion": 1
    },
    {
      "id": "c35ce278-eb86-45db-bc1b-923ccdb7298a",
      "name": "Receive Scoot webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        4496,
        2032
      ],
      "parameters": {
        "path": "scoot-transcription",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "d2a8a82e-6ba1-4e36-a3ee-9e2d6290bae5",
      "name": "Test with sample data",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        4496,
        2208
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "39e345df-7aed-45e3-9c9e-7721aa152242",
      "name": "Load sample transcript",
      "type": "n8n-nodes-base.code",
      "position": [
        4720,
        2208
      ],
      "parameters": {
        "jsCode": "// Sample transcript for testing\n// Replace with real Scoot data in production\n\nconst mockData = {\n  socialTranscriptionId: \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n  status: \"COMPLETE\",\n  name: \"Discovery Call - Acme Corp\",\n  company_name: \"Acme Corp\",\n  contact_name: \"Sarah Chen\",\n  contact_title: \"VP of Operations\",\n  contact_email: \"user@example.com\",\n  deal_id: \"deal_789\",\n  call_date: \"2024-01-15\",\n  rep_name: \"Mike Wilson\",\n  rep_email: \"user@example.com\",\n  transcript: `MIKE WILSON: Hey Sarah, thanks for taking the time today. I know you're busy, so I really appreciate you carving out 30 minutes for us.\n\nSARAH CHEN: Of course, Mike. I've been meaning to look into solutions like yours for a while now. We've been struggling with some operational challenges.\n\nMIKE WILSON: I'd love to hear more about that. What's been the biggest pain point for your team?\n\nSARAH CHEN: Honestly, it's the manual data entry. Our sales team spends probably 2-3 hours a day just updating spreadsheets and our CRM. It's killing productivity, and frankly, the data quality is terrible because people rush through it or skip it entirely.\n\nMIKE WILSON: That's really common. We hear that from a lot of companies your size. How big is your sales team currently?\n\nSARAH CHEN: We have about 45 reps across three regions. And we just hired a new CRO, Jennifer Martinez, who's really pushing for better data hygiene. She came from Salesforce actually, so she has high standards.\n\nMIKE WILSON: Jennifer Martinez - I think I've heard of her. She did great things at her previous company. So it sounds like there's executive sponsorship for solving this problem?\n\nSARAH CHEN: Definitely. Jennifer reports directly to our CEO, and she's made it clear that fixing our data infrastructure is a Q2 priority. We need to have something in place before our board meeting in June.\n\nMIKE WILSON: That's a clear timeline. Let me ask about budget - do you have a sense of what you're looking to invest in a solution like this?\n\nSARAH CHEN: We've allocated around $50,000 annually for this initiative. Jennifer got it approved in last quarter's budget cycle. But honestly, if something can really move the needle, there might be flexibility. We're losing way more than that in productivity.\n\nMIKE WILSON: That's helpful context. Now, I have to ask - are you looking at any other solutions right now?\n\nSARAH CHEN: We had a demo with Gong last week, and we've been using HubSpot for years but their automation features aren't cutting it anymore. We also looked at Clari but it felt too enterprise-heavy for our needs.\n\nMIKE WILSON: Good to know. What did you like or not like about Gong specifically?\n\nSARAH CHEN: Gong's call recording is great, but their pricing is steep - they quoted us $80,000 - and we're not sure we need all the revenue intelligence features. We really just need the automation piece to work well.\n\nMIKE WILSON: Makes sense. Our sweet spot is exactly that - focused automation without the bloat.\n\nSARAH CHEN: Okay, that's impressive. The automatic field population would save us so much time. But I do have a concern - how does this integrate with our existing Salesforce instance? We have a lot of custom fields.\n\nMIKE WILSON: Great question. We have a native Salesforce integration that maps to custom fields. Our implementation team would work with your Salesforce admin to set that up. Usually takes about 2 weeks.\n\nSARAH CHEN: Two weeks isn't bad. Our IT team is pretty stretched though - is this something that requires heavy IT involvement?\n\nMIKE WILSON: Minimal IT involvement actually. Most of the setup is done through our UI, and we handle the OAuth connection.\n\nSARAH CHEN: That's a relief. One more thing - we had a bad experience with our last vendor. Their support was terrible, took days to get responses. How does your support work?\n\nMIKE WILSON: Totally understand that concern. We have dedicated customer success managers for accounts your size, plus 24/7 chat support. Our average response time is under 2 hours.\n\nSARAH CHEN: That would actually be great. If you could send me a reference in a similar industry, that would help me make the case internally.\n\nMIKE WILSON: Absolutely. I'll send over a case study from a company in the manufacturing space.\n\nSARAH CHEN: Perfect. So what are next steps from here?\n\nMIKE WILSON: I'd suggest a couple things. First, I'll send you that case study and a detailed proposal based on what we discussed. Second, would it make sense to schedule a follow-up call with Jennifer? If she's the decision maker, I'd want to make sure she sees the value directly.\n\nSARAH CHEN: Yes, let's do that. Jennifer's calendar is pretty packed, but I can probably get 30 minutes next week. Thursday afternoon works best for her usually.\n\nMIKE WILSON: Thursday works great. I'll send over some times.\n\nSARAH CHEN: I'm cautiously optimistic about this. The demo looked good, the pricing is in our range, and if the integration is as smooth as you say, this could be exactly what we need.\n\nMIKE WILSON: That's great to hear. I'll get that proposal over to you by end of day tomorrow.\n\nSARAH CHEN: Sounds good. Thanks Mike!\n\nMIKE WILSON: Thank you Sarah. Talk soon!`,\n  is_mock: true\n};\n\nreturn [{ json: mockData }];"
      },
      "typeVersion": 2
    },
    {
      "id": "daf234dc-5709-4723-a723-9355cbd1663f",
      "name": "Fetch transcript from Scoot",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4960,
        2032
      ],
      "parameters": {
        "url": "=https://api.scoot.app/api/v1/transcription?socialTranscriptionId={{ $json.socialTranscriptionId }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "aaa869d4-3587-4cec-8c1e-24625726ac7d",
      "name": "Combine call metadata",
      "type": "n8n-nodes-base.code",
      "position": [
        5184,
        2112
      ],
      "parameters": {
        "jsCode": "// Combine webhook data with Scoot API response\nconst input = $input.item.json;\n\nif (input.is_mock) {\n  return [{ json: input }];\n}\n\nconst webhookData = $('Receive Scoot webhook').item?.json || {};\nconst scootData = input;\n\nreturn [{\n  json: {\n    ...webhookData,\n    ...scootData,\n    transcript: scootData.transcript || 'Transcript fetched from Scoot S3 bucket',\n    company_name: scootData.name?.replace('Discovery Call - ', '').replace('Sales Call - ', '') || 'Unknown Company',\n    rep_email: scootData.user?.emailAddress || 'user@example.com',\n    rep_name: scootData.user?.name || 'Sales Rep',\n    call_date: scootData.startDateTime?.split('T')[0] || new Date().toISOString().split('T')[0]\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "6619177b-3489-4f4b-b2ad-2b536389f385",
      "name": "Transcript ready?",
      "type": "n8n-nodes-base.if",
      "position": [
        5408,
        2112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "status-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "COMPLETE"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "fbeda433-46ba-4f9e-ae5b-35fb5e10adcf",
      "name": "Extract data with Gemini",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        5584,
        1904
      ],
      "parameters": {
        "text": "=Extract sales data from this transcript:\n\nCompany: {{ $json.company_name }}\nContact: {{ $json.contact_name || 'Unknown' }}\nDate: {{ $json.call_date }}\n\nTRANSCRIPT:\n{{ $json.transcript }}",
        "options": {
          "systemMessage": "You are a sales call analyst. Extract specific data points from sales call transcripts.\n\nReturn a valid JSON object with these fields:\n{\n  \"budget_confirmed\": boolean or null,\n  \"budget_amount\": number or null,\n  \"budget_notes\": string or null,\n  \"competitors_mentioned\": string[],\n  \"objections_raised\": string[],\n  \"timeline\": string or null,\n  \"decision_maker\": string or null,\n  \"next_steps\": string[],\n  \"pain_points\": string[],\n  \"buying_signals\": string[],\n  \"call_sentiment\": \"positive\" | \"neutral\" | \"negative\",\n  \"deal_stage_recommendation\": string\n}\n\nRules:\n- Only extract information explicitly stated\n- Use null for fields with no data\n- Keep strings under 100 chars\n- Return ONLY the JSON object"
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "23cc8b8b-bc53-4d37-aa34-c84286505558",
      "name": "Gemini 1.5 Flash",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        5584,
        2096
      ],
      "parameters": {
        "options": {
          "temperature": 0.3
        }
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "92d0e107-e3ef-4350-b357-e48b1efe7094",
      "name": "Validate extracted JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        5888,
        2000
      ],
      "parameters": {
        "jsCode": "// Parse and validate Gemini response\nconst input = $input.item.json;\nconst previousData = $('Combine call metadata').item.json;\n\nlet extracted;\n\ntry {\n  const aiResponse = input.text || input.content || input.message?.content || '';\n  let jsonString = aiResponse;\n  \n  if (jsonString.includes('```json')) {\n    jsonString = jsonString.replace(/```json\\n?/g, '').replace(/```\\n?/g, '');\n  } else if (jsonString.includes('```')) {\n    jsonString = jsonString.replace(/```\\n?/g, '');\n  }\n  \n  extracted = JSON.parse(jsonString.trim());\n} catch (error) {\n  extracted = {\n    budget_confirmed: null,\n    budget_amount: null,\n    budget_notes: null,\n    competitors_mentioned: [],\n    objections_raised: [],\n    timeline: null,\n    decision_maker: null,\n    next_steps: [],\n    pain_points: [],\n    buying_signals: [],\n    call_sentiment: 'neutral',\n    deal_stage_recommendation: 'Parsing error - review manually',\n    extraction_error: error.message\n  };\n}\n\nconst validated = {\n  budget_confirmed: typeof extracted.budget_confirmed === 'boolean' ? extracted.budget_confirmed : null,\n  budget_amount: typeof extracted.budget_amount === 'number' ? extracted.budget_amount : null,\n  budget_notes: typeof extracted.budget_notes === 'string' ? extracted.budget_notes.slice(0, 500) : null,\n  competitors_mentioned: Array.isArray(extracted.competitors_mentioned) ? extracted.competitors_mentioned : [],\n  objections_raised: Array.isArray(extracted.objections_raised) ? extracted.objections_raised : [],\n  timeline: typeof extracted.timeline === 'string' ? extracted.timeline : null,\n  decision_maker: typeof extracted.decision_maker === 'string' ? extracted.decision_maker : null,\n  next_steps: Array.isArray(extracted.next_steps) ? extracted.next_steps : [],\n  pain_points: Array.isArray(extracted.pain_points) ? extracted.pain_points : [],\n  buying_signals: Array.isArray(extracted.buying_signals) ? extracted.buying_signals : [],\n  call_sentiment: ['positive', 'neutral', 'negative'].includes(extracted.call_sentiment) ? extracted.call_sentiment : 'neutral',\n  deal_stage_recommendation: typeof extracted.deal_stage_recommendation === 'string' ? extracted.deal_stage_recommendation : null\n};\n\nreturn [{\n  json: {\n    call_id: previousData.socialTranscriptionId,\n    deal_id: previousData.deal_id || null,\n    company_name: previousData.company_name,\n    contact_name: previousData.contact_name || null,\n    contact_email: previousData.contact_email || null,\n    rep_name: previousData.rep_name,\n    rep_email: previousData.rep_email,\n    call_date: previousData.call_date,\n    ...validated,\n    extraction_timestamp: new Date().toISOString(),\n    extraction_error: extracted.extraction_error || null\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a1828d97-3d9c-4b0f-af2e-0337dcdc75eb",
      "name": "Format for CRM update",
      "type": "n8n-nodes-base.code",
      "position": [
        6112,
        2000
      ],
      "parameters": {
        "jsCode": "// Format data for CRM and email notification\nconst data = $input.item.json;\n\nconst crmUpdate = {\n  deal_id: data.deal_id,\n  amount: data.budget_amount,\n  close_date: data.timeline,\n  budget_confirmed__c: data.budget_confirmed,\n  budget_notes__c: data.budget_notes,\n  competitors__c: data.competitors_mentioned.join(', '),\n  objections__c: data.objections_raised.join(' | '),\n  decision_maker__c: data.decision_maker,\n  next_steps__c: data.next_steps.join(' | '),\n  pain_points__c: data.pain_points.join(' | '),\n  buying_signals__c: data.buying_signals.join(' | '),\n  last_call_sentiment__c: data.call_sentiment,\n  stage_recommendation__c: data.deal_stage_recommendation,\n  last_call_date__c: data.call_date,\n  last_call_id__c: data.call_id\n};\n\nconst emailSummary = `\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPOST-CALL SUMMARY: ${data.company_name}\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nCALL DETAILS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCompany: ${data.company_name}\nContact: ${data.contact_name || 'N/A'}\nDate: ${data.call_date}\nSentiment: ${data.call_sentiment.toUpperCase()}\n\nBUDGET\n\u2500\u2500\u2500\u2500\u2500\u2500\nConfirmed: ${data.budget_confirmed === true ? 'YES' : data.budget_confirmed === false ? 'NO' : 'Not discussed'}\nAmount: ${data.budget_amount ? '$' + data.budget_amount.toLocaleString() : 'Not specified'}\nNotes: ${data.budget_notes || 'None'}\n\nCOMPETITORS MENTIONED\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${data.competitors_mentioned.length > 0 ? data.competitors_mentioned.map(c => '\u2022 ' + c).join('\\n') : 'None mentioned'}\n\nOBJECTIONS RAISED\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${data.objections_raised.length > 0 ? data.objections_raised.map(o => '\u2022 ' + o).join('\\n') : 'None raised'}\n\nTIMELINE: ${data.timeline || 'Not specified'}\nDECISION MAKER: ${data.decision_maker || 'Not identified'}\n\nNEXT STEPS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${data.next_steps.length > 0 ? data.next_steps.map((s, i) => (i + 1) + '. ' + s).join('\\n') : 'None identified'}\n\nPAIN POINTS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${data.pain_points.length > 0 ? data.pain_points.map(p => '\u2022 ' + p).join('\\n') : 'None identified'}\n\nBUYING SIGNALS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${data.buying_signals.length > 0 ? data.buying_signals.map(b => '\u2713 ' + b).join('\\n') : 'None identified'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRECOMMENDATION: ${data.deal_stage_recommendation || 'No recommendation'}\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n`;\n\nreturn [{\n  json: {\n    ...data,\n    crm_update: crmUpdate,\n    email_summary: emailSummary\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "8090af00-8d4a-495e-93f2-fbef11386f1d",
      "name": "Update CRM (mock)",
      "type": "n8n-nodes-base.code",
      "position": [
        6320,
        2000
      ],
      "parameters": {
        "jsCode": "// Mock CRM update - replace with HubSpot/Salesforce/Pipedrive node\nconst data = $input.item.json;\n\nconst mockCrmResponse = {\n  success: true,\n  crm_provider: 'MOCK - Replace with real CRM',\n  deal_id: data.crm_update.deal_id || 'deal_' + Date.now(),\n  fields_updated: Object.keys(data.crm_update).filter(k => data.crm_update[k] !== null && data.crm_update[k] !== ''),\n  timestamp: new Date().toISOString()\n};\n\nreturn [{\n  json: {\n    ...data,\n    crm_response: mockCrmResponse\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7f517b87-fdcb-408e-b930-7b6cf9413c3e",
      "name": "Email summary to rep",
      "type": "n8n-nodes-base.gmail",
      "position": [
        6528,
        2000
      ],
      "parameters": {
        "sendTo": "={{ $json.rep_email }}",
        "message": "={{ $json.email_summary }}",
        "options": {},
        "subject": "=Call summary: {{ $json.company_name }} ({{ $json.call_sentiment }})"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "fe29d060-f2b5-4162-9f62-f5960a677246",
      "name": "Count retry attempts",
      "type": "n8n-nodes-base.code",
      "position": [
        5584,
        2256
      ],
      "parameters": {
        "jsCode": "// Track retry attempts for incomplete transcripts\nconst data = $input.item.json;\nconst currentRetryCount = data.retry_count || 0;\nconst maxRetries = 6;\n\nreturn [{\n  json: {\n    ...data,\n    retry_count: currentRetryCount + 1,\n    max_retries: maxRetries,\n    can_retry: currentRetryCount < maxRetries && data.status === 'PROCESSING'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "887699ea-3a93-4ba4-a407-3ba0d61655ba",
      "name": "Retries remaining?",
      "type": "n8n-nodes-base.if",
      "position": [
        5840,
        2224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "can-retry-check",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.can_retry }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f260acee-2f46-460c-ac70-c244e6b313ab",
      "name": "Wait 1 hour",
      "type": "n8n-nodes-base.wait",
      "position": [
        6064,
        2224
      ],
      "parameters": {
        "unit": "hours",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "db2cf67e-4bd3-4c2b-8f01-f8772ee781eb",
      "name": "Prepare retry request",
      "type": "n8n-nodes-base.code",
      "position": [
        6272,
        2224
      ],
      "parameters": {
        "jsCode": "// Prepare data for retry request\nconst data = $input.item.json;\n\nreturn [{\n  json: {\n    socialTranscriptionId: data.socialTranscriptionId,\n    retry_count: data.retry_count,\n    original_webhook_data: data.original_webhook_data || data\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "32069776-e022-467c-93a9-b88118339c53",
      "name": "Log failure",
      "type": "n8n-nodes-base.code",
      "position": [
        6096,
        2448
      ],
      "parameters": {
        "jsCode": "// Log failure after max retries exceeded\nconst data = $input.item.json;\n\nconst failureReason = data.status === 'FAILED' \n  ? 'Transcription failed in Scoot' \n  : `Max retries exceeded - still processing after ${data.max_retries} hours`;\n\nreturn [{\n  json: {\n    status: 'ABANDONED',\n    socialTranscriptionId: data.socialTranscriptionId,\n    failure_reason: failureReason,\n    retry_attempts: data.retry_count || 0,\n    timestamp: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "1209c3b3-57c8-4dbc-b59a-577a03130974",
  "connections": {
    "Wait 1 hour": {
      "main": [
        [
          {
            "node": "Prepare retry request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini 1.5 Flash": {
      "ai_languageModel": [
        [
          {
            "node": "Extract data with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Transcript ready?": {
      "main": [
        [
          {
            "node": "Extract data with Gemini",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Count retry attempts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update CRM (mock)": {
      "main": [
        [
          {
            "node": "Email summary to rep",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retries remaining?": {
      "main": [
        [
          {
            "node": "Wait 1 hour",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Count retry attempts": {
      "main": [
        [
          {
            "node": "Retries remaining?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine call metadata": {
      "main": [
        [
          {
            "node": "Transcript ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for CRM update": {
      "main": [
        [
          {
            "node": "Update CRM (mock)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare retry request": {
      "main": [
        [
          {
            "node": "Fetch transcript from Scoot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Scoot webhook": {
      "main": [
        [
          {
            "node": "Fetch transcript from Scoot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test with sample data": {
      "main": [
        [
          {
            "node": "Load sample transcript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load sample transcript": {
      "main": [
        [
          {
            "node": "Combine call metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate extracted JSON": {
      "main": [
        [
          {
            "node": "Format for CRM update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract data with Gemini": {
      "main": [
        [
          {
            "node": "Validate extracted JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch transcript from Scoot": {
      "main": [
        [
          {
            "node": "Combine call metadata",
            "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

Automatically extract sales insights from call transcripts and update your CRM.

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

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13
AI & RAG

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Agent, @Apify/N8N Nodes Apify, HTTP Request +3
AI & RAG

Zoho CRM - Smart Meeting Scheduler. Uses zohoCrm, googleCalendar, httpRequest, agent. Webhook trigger; 23 nodes.

Zoho Crm, Google Calendar, HTTP Request +3
AI & RAG

This automated n8n workflow detects and manages fraudulent booking transactions through comprehensive AI-powered analysis and multi-layered security checks. The system processes incoming travel bookin

HTTP Request, Google Sheets, Agent +2
AI & RAG

This workflow uses AI to automatically analyze a candidate’s CV against any job posting. It extracts key skills, requirements, and gaps, then generates a clear fit summary, recommendations, and optimi

HTTP Request, Google Gemini Chat, Output Parser Structured +2