{
  "id": "OcCtnHxSp53PVvYz",
  "name": "Transcript_assistant",
  "tags": [],
  "nodes": [
    {
      "id": "7149568c-e6da-45f9-aced-41f6d03b7f4a",
      "name": "Setup Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -192
      ],
      "parameters": {
        "width": 448,
        "height": 724,
        "content": "## \ud83d\udcdd Post-Call Glossary \u2014 Setup\n\n**How it works:**\nAfter a meeting, feed in the full transcript/minutes (manually via the form, or automatically via webhook from your transcription tool). The Agent scans the whole text and extracts ALL technical terms/acronyms it finds, then sends you a single Telegram message with the full glossary \u2014 or a short \"nothing to add\" message if no relevant terms are found.\n\n**Two ways to trigger:**\n1. **Form** \u2014 open the form URL, paste the transcript, submit. Good for manual review.\n2. **Webhook** \u2014 POST `{ \"transcript\": \"...\", \"sessionId\": \"optional\" }` to the webhook URL. Good for connecting Otter/Fireflies/Recall.ai or a Drive-based automation.\n\n**Required setup:**\n1. Anthropic (or any other LLM) credentials on the Chat Model node\n2. Telegram Bot credentials on both Telegram nodes + your chat_id\n\n**Note:** both triggers wire directly into the same Agent \u2014 a single execution only ever comes from one trigger at a time, so no merge step is needed."
      },
      "typeVersion": 1
    },
    {
      "id": "60480a9e-2663-4ff7-897e-9ce66d3105b7",
      "name": "Section: Inputs",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -192
      ],
      "parameters": {
        "width": 412,
        "height": 836,
        "content": "### 1\ufe0f\u20e3 Inputs\n\nTwo independent entry points, normalized into the same shape (`transcript`, `sessionId`) before reaching the Agent:\n\n- **Form**: for pasting a transcript by hand right after a call.\n- **Webhook**: for wiring up an automated transcription tool.\n\nOnly one trigger fires per execution \u2014 there's no merge step, both \"Normalize\" nodes feed the Agent directly."
      },
      "typeVersion": 1
    },
    {
      "id": "603bf48e-736f-4131-954f-464506cbe1df",
      "name": "Form: Paste Transcript",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        48,
        288
      ],
      "parameters": {
        "options": {},
        "formTitle": "Post-Call Glossary Extractor",
        "formFields": {
          "values": [
            {
              "fieldType": "textarea",
              "fieldLabel": "Transcript",
              "requiredField": true
            },
            {
              "fieldLabel": "Session ID (optional)"
            }
          ]
        },
        "formDescription": "Paste the meeting transcript/minutes below to extract a glossary of technical terms."
      },
      "typeVersion": 2.2
    },
    {
      "id": "898cfe95-da49-42d5-9925-fc30d4eb3c01",
      "name": "Webhook: Receive Transcript",
      "type": "n8n-nodes-base.webhook",
      "position": [
        48,
        480
      ],
      "parameters": {
        "path": "post-call-glossary",
        "options": {
          "rawBody": true
        },
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "51b8b51d-02c2-4380-815e-b58bf5bdb0c7",
      "name": "Normalize: Form Input",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        288
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assign-transcript",
              "name": "transcript",
              "type": "string",
              "value": "={{ $json.transcript }}"
            },
            {
              "id": "assign-session",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.sessionId || 'form-session' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f63ac55f-616b-4d2d-a9f2-e757973a2e53",
      "name": "Normalize: Webhook Input",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assign-transcript-wh",
              "name": "transcript",
              "type": "string",
              "value": "={{ $json.body.transcript }}"
            },
            {
              "id": "assign-session-wh",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.body.sessionId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e6d9c7ee-8da3-4ac0-b6d1-8d8afb4b8833",
      "name": "Section: AI Extraction",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        16
      ],
      "parameters": {
        "width": 516,
        "height": 736,
        "content": "### 2\ufe0f\u20e3 AI Extraction\n\nThe Agent reads the full transcript in one pass and returns a strict JSON block listing every distinct technical term/acronym found, each with a short definition. No streaming, no chunking \u2014 full context in, full glossary out.\n\nSwap the Chat Model node for any provider you like; only the Agent's system prompt matters for output format."
      },
      "typeVersion": 1
    },
    {
      "id": "628e2a8f-6844-4a99-9e19-257219109fb4",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        496,
        384
      ],
      "parameters": {
        "text": "={{ $json.transcript }}",
        "options": {
          "systemMessage": "You are an assistant that reviews meeting transcripts/minutes after the call has ended. Read the ENTIRE text provided by the user and identify ALL technical concepts, complex acronyms, or methodologies mentioned (e.g. 'Confusion matrix', 'EBITDA', 'SLA').\n\nRules:\n1. ALWAYS respond ONLY with a valid JSON object wrapped in a markdown code block.\n2. The structure must always be:\n```json\n{\n  \"found\": true or false,\n  \"terms\": [\n    { \"term\": \"Term name\", \"definition\": \"Concise definition (max 25 words)\" }\n  ]\n}\n```\n3. If you find one or more relevant terms, set \"found\": true and list every distinct term once in \"terms\" (no duplicates, even if mentioned multiple times in the text).\n4. If you find NO relevant terms, respond with `{ \"found\": false, \"terms\": [] }`.\n5. Do not add any explanation or text outside the json block."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "0ab46341-eee6-4464-9aff-7b15fc7c96de",
      "name": "Section: Output Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        -96
      ],
      "parameters": {
        "width": 592,
        "height": 736,
        "content": "### 3\ufe0f\u20e3 Output Routing\n\nThe raw Agent output is parsed into structured JSON, then routed:\n\n- **Terms found** \u2192 formatted into one numbered glossary message and forwarded to Telegram\n- **Nothing relevant** \u2192 a short \"nothing to add\" Telegram message instead\n\nEither way, you get a Telegram notification confirming the review happened."
      },
      "typeVersion": 1
    },
    {
      "id": "d803108f-b76b-42a3-9dcc-8df8faedf81f",
      "name": "Extract JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        384
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const text = $input.item.json.output || '';\nconst match = text.match(/```json\\s*([\\s\\S]*?)\\s*```/);\n\nif (match) {\n  try {\n    const parsed = JSON.parse(match[1].trim());\n    if (!Array.isArray(parsed.terms)) parsed.terms = [];\n    return { json: parsed };\n  } catch (e) {\n    return { json: { found: false, terms: [], error: 'Parsing error: ' + e.message, raw: text } };\n  }\n}\n\nreturn { json: { found: false, terms: [], error: 'No JSON block found', raw: text } };"
      },
      "typeVersion": 2
    },
    {
      "id": "87439025-10a8-455e-9828-3d05793282d2",
      "name": "Any Terms Found?",
      "type": "n8n-nodes-base.if",
      "position": [
        1072,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "bfa86fa7-c104-44b4-8db6-bdc56dfa2839",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.found }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "93bbfe86-a545-471c-864b-553f2de11b0d",
      "name": "Format Glossary Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1296,
        288
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const terms = $input.item.json.terms || [];\n\nconst lines = terms.map((t, i) => `${i + 1}. *${t.term}*\\n   ${t.definition}`);\n\nconst message = `\ud83d\udcda *Post-Call Glossary*\\n_${terms.length} term${terms.length === 1 ? '' : 's'} found_\\n\\n${lines.join('\\n\\n')}`;\n\nreturn { json: { message } };"
      },
      "typeVersion": 2
    },
    {
      "id": "ac4798f6-3165-49ad-8eaa-f8de7bb509ac",
      "name": "Anthropic Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        576,
        608
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-6",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {}
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.5
    },
    {
      "id": "ad1b61fc-b6de-4880-a1a5-369d83b94243",
      "name": "OK - Send to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1520,
        288
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "<CHAT_ID>",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cbfd0892-0b3b-4480-a27c-14562271b004",
      "name": "KO - Send to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1296,
        480
      ],
      "parameters": {
        "text": "=Nothing to add ",
        "chatId": "<CHAT_ID>",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "7a50129a-5727-4143-abc5-7ce88992b6bd",
  "nodeGroups": [],
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Extract JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract JSON": {
      "main": [
        [
          {
            "node": "Any Terms Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Any Terms Found?": {
      "main": [
        [
          {
            "node": "Format Glossary Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "KO - Send to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Normalize: Form Input": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Form: Paste Transcript": {
      "main": [
        [
          {
            "node": "Normalize: Form Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Glossary Message": {
      "main": [
        [
          {
            "node": "OK - Send to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize: Webhook Input": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook: Receive Transcript": {
      "main": [
        [
          {
            "node": "Normalize: Webhook Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}