{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "2e130d5f-e4cc-4f22-a4aa-64d6df245827",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -176
      ],
      "parameters": {
        "color": 4,
        "width": 508,
        "height": 952,
        "content": "## Meeting Talk-Time Analyzer \u2014 Fireflies + Telegram\n\nFor managers, coaches, and team leads who want to know exactly how much each person spoke in a meeting \u2014 and whether anyone dominated. When Fireflies finishes transcribing a meeting, this workflow fetches the built-in speaker analytics, builds a visual ASCII bar chart per speaker, detects if anyone crossed the dominance threshold, and sends a complete formatted report to Telegram instantly. No AI, no spreadsheets \u2014 just pure meeting analytics delivered to your phone.\n\n## How it works\n- **1. Webhook \u2014 Fireflies Transcript Done** receives the meetingId from Fireflies on transcription complete\n- **2. Set \u2014 Config Values** stores your Fireflies API key, Telegram chat ID, and the dominance threshold percentage (default 70)\n- **3. HTTP \u2014 Fetch Speaker Analytics** calls the Fireflies GraphQL API and returns per-speaker data: talk time percentage, word count, pace, questions asked, longest monologue, speaking turns, and filler words\n- **4. Code \u2014 Analyze Speaker Data** sorts speakers by talk time, builds a visual bar chart for each (10 blocks = 100%), detects if anyone crossed the threshold, calculates total questions, and builds the complete Telegram message\n- **5. Telegram \u2014 Send Talk-Time Report** delivers the formatted report with speaker breakdown and dominance alert to your Telegram chat\n\n## Set up steps\n1. Activate the workflow and copy the webhook URL from node 1\n2. In Fireflies \u2014 go to Settings, Developer Settings, Webhooks, and paste the URL\n3. In **2. Set \u2014 Config Values** \u2014 replace YOUR_FIREFLIES_API_KEY and YOUR_TELEGRAM_CHAT_ID\n4. In **5. Telegram \u2014 Send Talk-Time Report** \u2014 connect your Telegram Bot API credential and send /start to your bot before testing\n5. Optionally adjust dominanceThreshold in node 2 \u2014 lower for stricter flagging, higher for looser"
      },
      "typeVersion": 1
    },
    {
      "id": "3a7f5c69-4f01-4c4e-8133-723241eb7f28",
      "name": "Section \u2014 Webhook Receipt and Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        128
      ],
      "parameters": {
        "color": 5,
        "width": 548,
        "height": 436,
        "content": "## Webhook Receipt and Config\nFireflies POSTs a meetingId here on transcription complete. Config stores your Fireflies API key, Telegram chat ID, and the dominance threshold. The meetingId is extracted inline from the webhook payload."
      },
      "typeVersion": 1
    },
    {
      "id": "810e93f7-f268-45e9-a0c9-3e93ca222b24",
      "name": "Section \u2014 Speaker Analytics Fetch and Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        128
      ],
      "parameters": {
        "color": 6,
        "width": 484,
        "height": 436,
        "content": "## Speaker Analytics Fetch and Analysis\nFetches Fireflies per-speaker analytics: talk time percentage, word count, pace, questions, longest monologue, turns, and filler words. Builds ASCII bar charts, detects dominance, and assembles the full Telegram report."
      },
      "typeVersion": 1
    },
    {
      "id": "53539469-e97f-4a69-8d2e-9bfba52db1cc",
      "name": "Section \u2014 Telegram Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        128
      ],
      "parameters": {
        "color": 4,
        "width": 468,
        "height": 436,
        "content": "## Telegram Delivery\nSends the formatted talk-time report with visual bar chart and dominance alert to your Telegram chat instantly."
      },
      "typeVersion": 1
    },
    {
      "id": "7fb875f4-ade7-4dae-88fe-4bee95986325",
      "name": "1. Webhook \u2014 Fireflies Transcript Done",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -64,
        304
      ],
      "parameters": {
        "path": "fireflies-talk-time",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1.1
    },
    {
      "id": "1f866f76-e137-4c35-b298-5b561935b672",
      "name": "2. Set \u2014 Config Values",
      "type": "n8n-nodes-base.set",
      "position": [
        256,
        304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "firefliesApiKey",
              "type": "string",
              "value": "YOUR_FIREFLIES_API_KEY"
            },
            {
              "id": "cfg-002",
              "name": "telegramChatId",
              "type": "string",
              "value": "YOUR_TELEGRAM_CHAT_ID"
            },
            {
              "id": "cfg-003",
              "name": "dominanceThreshold",
              "type": "string",
              "value": "70"
            },
            {
              "id": "cfg-004",
              "name": "meetingId",
              "type": "string",
              "value": "={{ $json.meetingId || $json.body?.meetingId || $json.data?.meetingId || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "63cad5d6-d3e9-4a58-bba9-7fdc4e5778be",
      "name": "3. HTTP \u2014 Fetch Speaker Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        576,
        304
      ],
      "parameters": {
        "url": "https://api.fireflies.ai/graphql",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"query\": \"query GetTranscript($id: String!) { transcript(id: $id) { id title date duration participants transcript_url analytics { speakers { speaker_id name duration word_count longest_monologue monologues_count filler_words questions duration_pct words_per_minute } } } }\",\n  \"variables\": {\n    \"id\": \"{{ $json.meetingId }}\"\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.firefliesApiKey }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d05eb799-0039-495c-a918-a9b4f7832408",
      "name": "4. Code \u2014 Analyze Speaker Data",
      "type": "n8n-nodes-base.code",
      "position": [
        832,
        304
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst config = $('2. Set \u2014 Config Values').item.json;\n\nconst t = response?.data?.transcript;\nif (!t || !t.id) throw new Error('Transcript not found for meetingId: ' + config.meetingId);\n\nconst speakers = t.analytics?.speakers || [];\n\nif (speakers.length === 0) {\n  throw new Error('No speaker analytics available for this meeting. Fireflies may not have detected speakers.');\n}\n\nconst threshold = parseInt(config.dominanceThreshold) || 70;\nconst meetingDurationMin = Math.round((t.duration || 0) / 60);\n\nconst meetingDate = t.date\n  ? new Date(t.date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })\n  : new Date().toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });\n\n// Sort speakers by duration_pct descending\nconst sorted = [...speakers].sort((a, b) =>\n  (b.duration_pct || 0) - (a.duration_pct || 0)\n);\n\n// Build speaker rows\nlet speakerLines = '';\nlet dominatingSpeaker = null;\nlet totalQuestions = 0;\n\nsorted.forEach((sp, index) => {\n  const name = sp.name || 'Speaker ' + (index + 1);\n  const pct = Math.round(sp.duration_pct || 0);\n  const words = sp.word_count || 0;\n  const wpm = Math.round(sp.words_per_minute || 0);\n  const questions = sp.questions || 0;\n  const longestMono = Math.round((sp.longest_monologue || 0) / 60);\n  const monologues = sp.monologues_count || 0;\n  const fillers = sp.filler_words || 0;\n  const talkMin = Math.round((sp.duration || 0) / 60);\n\n  totalQuestions += questions;\n\n  // Build visual talk bar (10 chars = 100%)\n  const barFilled = Math.round(pct / 10);\n  const bar = '\u2588'.repeat(barFilled) + '\u2591'.repeat(10 - barFilled);\n\n  // Flag dominant speaker\n  const dominanceFlag = pct >= threshold ? ' DOMINANT' : '';\n  if (pct >= threshold && !dominatingSpeaker) dominatingSpeaker = name;\n\n  speakerLines +=\n    (index + 1) + '. ' + name + dominanceFlag + '\\n' +\n    '   ' + bar + ' ' + pct + '% (' + talkMin + ' min)\\n' +\n    '   Words: ' + words + ' | Pace: ' + wpm + ' wpm | Questions: ' + questions + '\\n' +\n    '   Longest speech: ' + longestMono + ' min | Turns: ' + monologues + ' | Fillers: ' + fillers + '\\n\\n';\n});\n\n// Build alert message\nlet alertLine = '';\nif (dominatingSpeaker) {\n  alertLine = 'ALERT: ' + dominatingSpeaker + ' dominated this meeting at ' +\n    Math.round(sorted[0].duration_pct || 0) + '% talk time. Consider giving others more space.\\n\\n';\n} else {\n  alertLine = 'Balance: Talk time was reasonably distributed across participants.\\n\\n';\n}\n\n// Build full Telegram message\nconst message =\n  'Meeting Talk-Time Report\\n' +\n  '\\n' +\n  'Meeting: ' + (t.title || 'Untitled') + '\\n' +\n  'Date: ' + meetingDate + ' | Duration: ' + meetingDurationMin + ' min\\n' +\n  'Total Questions Asked: ' + totalQuestions + '\\n' +\n  '\\n' +\n  alertLine +\n  'SPEAKER BREAKDOWN\\n\\n' +\n  speakerLines +\n  'Fireflies: ' + (t.transcript_url || 'Not available');\n\nreturn [{\n  json: {\n    telegramMessage: message,\n    telegramChatId: config.telegramChatId,\n    hasDominance: !!dominatingSpeaker,\n    meetingTitle: t.title || 'Untitled'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9792fd7d-f5ea-47d4-976a-af4fae0a7edf",
      "name": "5. Telegram \u2014 Send Talk-Time Report",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1232,
        304
      ],
      "parameters": {
        "text": "={{ $json.telegramMessage }}",
        "chatId": "={{ $json.telegramChatId }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    }
  ],
  "connections": {
    "2. Set \u2014 Config Values": {
      "main": [
        [
          {
            "node": "3. HTTP \u2014 Fetch Speaker Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. Code \u2014 Analyze Speaker Data": {
      "main": [
        [
          {
            "node": "5. Telegram \u2014 Send Talk-Time Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. HTTP \u2014 Fetch Speaker Analytics": {
      "main": [
        [
          {
            "node": "4. Code \u2014 Analyze Speaker Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. Webhook \u2014 Fireflies Transcript Done": {
      "main": [
        [
          {
            "node": "2. Set \u2014 Config Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}