AutomationFlowsData & Sheets › Generate Uk M&a Research, Pitch Decks and Briefs From Slack Using Anthropic…

Generate Uk M&a Research, Pitch Decks and Briefs From Slack Using Anthropic…

Original n8n title: Generate Uk M&a Research, Pitch Decks and Briefs From Slack Using Anthropic and Google Docs/slides

ByPankstr @pankstr on n8n.io

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and produces structured outputs in Google Docs, Google Slides, Google Sheets, and…

Event trigger★★★★★ complexity88 nodesHTTP RequestGoogle DriveGoogle DocsPostgresGoogle SheetsGoogle SlidesSlackSlack Trigger
Data & Sheets Trigger: Event Nodes: 88 Complexity: ★★★★★ Added:
Generate Uk M&a Research, Pitch Decks and Briefs From Slack Using Anthropic… — n8n workflow card showing HTTP Request, Google Drive, Google Docs integration

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

This workflow follows the Google Docs → Google Drive 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": "DdoucNGUKuzKpOCh",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Finance Research Analyst v5 - Production",
  "tags": [
    {
      "id": "JaNCWi5XTaMmf9J9",
      "name": "AI Employee",
      "createdAt": "2026-04-03T11:53:13.004Z",
      "updatedAt": "2026-04-03T11:53:13.004Z"
    }
  ],
  "nodes": [
    {
      "id": "9cf91144-5158-4ae3-af6c-ad1dce27c26c",
      "name": "Valid Slack Message?",
      "type": "n8n-nodes-base.if",
      "position": [
        -3168,
        1888
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "no-bot-id",
              "operator": {
                "type": "string",
                "operation": "notExists",
                "singleValue": true
              },
              "leftValue": "={{ $json.bot_id }}",
              "rightValue": ""
            },
            {
              "id": "has-text",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.text }}",
              "rightValue": ""
            },
            {
              "id": "is-message",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.type }}",
              "rightValue": "message"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2d46e584-601a-4778-a11a-1c110173039d",
      "name": "Prepare Slack Context",
      "type": "n8n-nodes-base.code",
      "position": [
        -2944,
        1888
      ],
      "parameters": {
        "jsCode": "const text = ($json.text || '').trim();\nreturn [{ json: {\n  raw_text: text,\n  channel: $json.channel,\n  message_ts: $json.ts,\n  thread_ts: $json.thread_ts || $json.ts,\n  user: $json.user,\n  slack_event: $json,\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "2d4d8426-4e76-4a36-8dad-be131044aece",
      "name": "Build Intent Request",
      "type": "n8n-nodes-base.code",
      "position": [
        -2720,
        1888
      ],
      "parameters": {
        "jsCode": "const body = {\n  model: 'anthropic/claude-haiku-4.5',\n  temperature: 0,\n  messages: [\n    {\n      role: 'system',\n      content: [\n        'You route requests for a UK boutique M&A finance research analyst.',\n        'Return raw JSON only.',\n        'Use intent values: research, pitch, brief, unknown.',\n        'Extract only what is present or safely inferable from the message text.',\n        'Do not claim to know Companies House numbers or websites yet.',\n        'For pitch requests, capture the company_name.',\n        'For brief requests, capture the sector.',\n        'Ensure sector, ticker  and company_url are derived on your own if not provided'\n      ].join(' '),\n    },\n    {\n      role: 'user',\n      content: $json.raw_text,\n    },\n  ],\n  response_format: {\n    type: 'json_schema',\n    json_schema: {\n      name: 'finance_research_intent',\n      strict: true,\n      schema: {\n        type: 'object',\n        additionalProperties: false,\n        properties: {\n          intent: { type: 'string', enum: ['research', 'pitch', 'brief', 'unknown'] },\n          company_name: { type: ['string', 'null'] },\n          sector: { type: ['string', 'null'] },\n          ticker: { type: ['string', 'null'] },\n          company_url: { type: ['string', 'null'] },\n          confidence: { type: ['number', 'null'] },\n          reasoning_note: { type: ['string', 'null'] }\n        },\n        required: ['intent', 'company_name', 'sector', 'ticker', 'company_url', 'confidence', 'reasoning_note']\n      }\n    }\n  }\n};\nreturn [{ json: { ...$json, openrouter_body: body } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "9bf6a6d3-73bd-4b67-8e17-6f7cb2bcb1ed",
      "name": "Intent Classifier",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2496,
        1888
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{$json.openrouter_body}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openRouterApi"
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "d1779c3b-9f06-4212-afaf-97615fe5c61d",
      "name": "Parse Intent",
      "type": "n8n-nodes-base.code",
      "position": [
        -2272,
        1888
      ],
      "parameters": {
        "jsCode": "function parseJson(text) {\n  if (!text) return {};\n  const cleaned = String(text).trim().replace(/^```json\\s*/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n  const first = cleaned.indexOf('{');\n  const last = cleaned.lastIndexOf('}');\n  const candidate = first >= 0 && last >= first ? cleaned.slice(first, last + 1) : cleaned;\n  return JSON.parse(candidate);\n}\nconst content = $json.choices?.[0]?.message?.content || '{}';\nconst parsed = parseJson(content);\nreturn [{ json: {\n  raw_text: $('Prepare Slack Context').item.json.raw_text,\n  channel: $('Prepare Slack Context').item.json.channel,\n  thread_ts: $('Prepare Slack Context').item.json.thread_ts,\n  user: $('Prepare Slack Context').item.json.user,\n  intent: parsed.intent || 'unknown',\n  company_name: parsed.company_name || '',\n  sector: parsed.sector || '',\n  ticker: parsed.ticker || '',\n  company_url: parsed.company_url || '',\n  confidence: parsed.confidence ?? null,\n  reasoning_note: parsed.reasoning_note || '',\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "64fa037a-374d-4206-9b6f-e0331154ea8c",
      "name": "Intent Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -2048,
        1856
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "research",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "research"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "pitch",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "pitch"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "brief",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "brief"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "unknown",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "unknown"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "093d6f60-4315-4d8a-97ea-fb5b658d633f",
      "name": "Unknown Intent Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2896,
        1552
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $json.channel,\n  thread_ts: $json.thread_ts,\n  reply_text: 'I could not route that request. Try one of: `Research Monzo, fintech`, `Prepare pitch for Monzo`, or `Industry briefing UK fintech`.'\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "060863ca-27ea-4610-b0d5-d1407572c863",
      "name": "Has Research Inputs?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1824,
        976
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.company_name }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "6c0b00e4-5501-4443-8d97-72d131722d48",
      "name": "Research Missing Input Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        1296
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $json.channel,\n  thread_ts: $json.thread_ts,\n  reply_text: 'Research requests need a company name. Example: `Research Revolut, fintech`.'\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "e22e21d1-6b30-47df-ac1b-712c94cb5853",
      "name": "Companies House Search",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1600,
        592
      ],
      "parameters": {
        "url": "=https://api.company-information.service.gov.uk/search/companies?q={{ encodeURIComponent($json.company_name) }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "f89d57fe-141b-4ce1-a235-e753d64a4b69",
      "name": "Pick Best Company Match",
      "type": "n8n-nodes-base.code",
      "position": [
        -1376,
        592
      ],
      "parameters": {
        "jsCode": "const input = $('Has Research Inputs?').item.json;\nconst target = (input.company_name || '').toLowerCase().trim();\nconst items = Array.isArray($json.items) ? $json.items : [];\nconst ranked = items\n  .filter((item) => item.company_number)\n  .map((item) => {\n    const title = String(item.title || '').toLowerCase();\n    let score = 0;\n    if (item.company_status === 'active') score += 5;\n    if (title === target) score += 20;\n    if (title.startsWith(target)) score += 10;\n    if (title.includes(target)) score += 5;\n    if (item.address_snippet && String(item.address_snippet).toLowerCase().includes('united kingdom')) score += 1;\n    return { item, score };\n  })\n  .sort((a, b) => b.score - a.score);\nconst best = ranked[0]?.item || null;\nreturn [{ json: {\n  ...input,\n  match_found: Boolean(best),\n  company_number: best?.company_number || '',\n  matched_title: best?.title || '',\n  matched_company_status: best?.company_status || '',\n  matched_address: best?.address_snippet || '',\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0b293fb2-bbe2-46e3-9345-080b15d5bb4b",
      "name": "Company Match Found?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1152,
        592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.match_found }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "71e8da5c-696f-4a57-913a-6f6abe126de1",
      "name": "Company Not Found Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        592
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $json.channel,\n  thread_ts: $json.thread_ts,\n  reply_text: `Companies House could not find a reliable UK company match for *${$json.company_name}*. Check spelling or provide the registered company name.`\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "9698c1d5-2c0b-414e-b076-5ab3fa636c76",
      "name": "Companies House Profile",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -928,
        64
      ],
      "parameters": {
        "url": "=https://api.company-information.service.gov.uk/company/{{ $json.company_number }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "7e62dba8-cd88-4a31-89f8-867a08482fe7",
      "name": "Wrap Profile",
      "type": "n8n-nodes-base.code",
      "position": [
        -704,
        64
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'profile', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "733811d2-c4eb-4ab9-9529-b0be02d31ab1",
      "name": "Companies House Officers",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -928,
        256
      ],
      "parameters": {
        "url": "=https://api.company-information.service.gov.uk/company/{{ $json.company_number }}/officers",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "dcb05c0f-2b09-4a40-bb70-70e75aec88c4",
      "name": "Wrap Officers",
      "type": "n8n-nodes-base.code",
      "position": [
        -704,
        256
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'officers', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "2037a207-6dba-48af-bd42-7935ccd25756",
      "name": "Companies House Filings",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -896,
        448
      ],
      "parameters": {
        "url": "=https://api.company-information.service.gov.uk/company/{{ $json.company_number }}/filing-history",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "db395818-104e-4c9b-957b-7d9fdc9e6f41",
      "name": "Wrap Filings",
      "type": "n8n-nodes-base.code",
      "position": [
        -720,
        448
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'filings', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0b5bf1ad-a904-4385-a6ae-1dae8c2a26a2",
      "name": "Company News",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -912,
        768
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v2/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={{({ query: `${$json.company_name} ${$json.sector || \"\"} recent company news funding acquisitions`, sources: [\"web\", \"news\"], limit: 8, scrapeOptions: { onlyMainContent: true, formats: [\"markdown\"] } })}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "2d1842f6-1754-4590-b92d-04f8f26e44c8",
      "name": "Wrap Research News",
      "type": "n8n-nodes-base.code",
      "position": [
        -624,
        800
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'news', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0aaf7ac9-a471-4be4-adc3-b1b5b8b99bf9",
      "name": "Website Search",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -880,
        608
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v2/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={{({ query: $json.company_url ? `${$json.company_name} ${$json.company_url}` : `${$json.company_name} official website`, sources: [\"web\"], limit: 5, scrapeOptions: { onlyMainContent: true, formats: [\"markdown\"] } })}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "e0fbbc58-53e4-48c0-87ed-1338e3685ca9",
      "name": "Wrap Website",
      "type": "n8n-nodes-base.code",
      "position": [
        -640,
        624
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'website', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "fbe54b18-432c-4f6c-9fcb-23855fc8bf98",
      "name": "Has Ticker?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1312,
        896
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.ticker }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ed0af8bf-7a2c-499a-b89c-6be804a25b90",
      "name": "Alpha Vantage Overview",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -912,
        928
      ],
      "parameters": {
        "url": "=https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={{ encodeURIComponent($json.ticker) }}&apikey=KEYHERE",
        "options": {}
      },
      "typeVersion": 4.4
    },
    {
      "id": "401249fc-1b4c-4ae8-8d04-58a4b9d39f32",
      "name": "Wrap Alpha Vantage",
      "type": "n8n-nodes-base.code",
      "position": [
        -624,
        960
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'alphavantage', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "746753eb-a9e6-407a-9189-c347941a9231",
      "name": "No Market Data Placeholder",
      "type": "n8n-nodes-base.code",
      "position": [
        -80,
        992
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'alphavantage', data: { skipped: true, reason: 'No public ticker supplied or inferred.' } } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "4344ed39-de97-4bd3-92c4-94e60e9415a7",
      "name": "Merge Research 1",
      "type": "n8n-nodes-base.merge",
      "position": [
        -480,
        144
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "5f5a5dd5-9dfa-4dcf-89ea-a1fb572d34b5",
      "name": "Merge Research 2",
      "type": "n8n-nodes-base.merge",
      "position": [
        -256,
        240
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "e72d8b28-ac7d-446c-9559-a03449845f7e",
      "name": "Merge Research 3",
      "type": "n8n-nodes-base.merge",
      "position": [
        -32,
        240
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "0dee524d-0092-4cc7-af2e-985091c7ddb3",
      "name": "Merge Research 4",
      "type": "n8n-nodes-base.merge",
      "position": [
        192,
        240
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "b50e0fcd-15c8-478c-91a8-94c20ea6ebe8",
      "name": "Merge Research 5",
      "type": "n8n-nodes-base.merge",
      "position": [
        416,
        400
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "86b7b978-04db-4bd3-810b-480e62455c07",
      "name": "Build Research Bundle",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        400
      ],
      "parameters": {
        "jsCode": "const routed = $('Pick Best Company Match').item.json;\nconst bundle = {\n  channel: routed.channel,\n  thread_ts: routed.thread_ts,\n  raw_text: routed.raw_text,\n  company_name: routed.company_name,\n  sector: routed.sector,\n  ticker: routed.ticker,\n  company_url: routed.company_url,\n  company_number: routed.company_number,\n  matched_title: routed.matched_title,\n  sources: {},\n  warnings: [],\n};\nfor (const item of $input.all()) {\n  const source = item.json.source;\n  if (source) bundle.sources[source] = item.json.data;\n}\nif (bundle.sources.alphavantage?.skipped) bundle.warnings.push(bundle.sources.alphavantage.reason);\nconst websiteHit = Array.isArray(bundle.sources.website?.data) ? bundle.sources.website.data[0] : bundle.sources.website?.data?.[0];\nconst websiteUrl = websiteHit?.url || websiteHit?.metadata?.sourceURL || bundle.company_url || '';\nconst marketCapRaw = bundle.sources.alphavantage?.MarketCapitalization || bundle.sources.alphavantage?.['Global Quote']?.['05. price'] || '';\nbundle.market_cap = marketCapRaw && marketCapRaw !== 'None' ? marketCapRaw : '';\nbundle.website_url = websiteUrl;\nreturn [{ json: bundle }];"
      },
      "typeVersion": 2
    },
    {
      "id": "587e96ce-3c50-407c-8fc9-1c6f109ba499",
      "name": "Build Research Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        400
      ],
      "parameters": {
        "jsCode": "const body = {\n  model: 'anthropic/claude-sonnet-4.5',\n  temperature: 0.2,\n  messages: [\n    {\n      role: 'system',\n      content: [\n        'You are a junior finance research analyst for a UK boutique M&A advisory.',\n        'Use only the supplied source data.',\n        'If a field is missing, say it is unavailable rather than inventing it.',\n        'Return raw JSON only.'\n      ].join(' '),\n    },\n    {\n      role: 'user',\n      content: `Create a company research pack for ${$json.company_name} in sector ${$json.sector || 'unknown'} using this source bundle:\\n${JSON.stringify($json)}`,\n    },\n  ],\n  response_format: {\n    type: 'json_schema',\n    json_schema: {\n      name: 'finance_research_output',\n      strict: true,\n      schema: {\n        type: 'object',\n        additionalProperties: false,\n        properties: {\n          profile_markdown: { type: 'string' },\n          slack_summary: { type: 'string' },\n          market_cap_summary: { type: 'string' },\n          risks_summary: { type: 'string' }\n        },\n        required: ['profile_markdown', 'slack_summary', 'market_cap_summary', 'risks_summary']\n      }\n    }\n  }\n};\nreturn [{ json: { ...$json, openrouter_body: body } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a56a308a-f7bf-414c-a648-26c94fba8ccd",
      "name": "Research AI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1088,
        400
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{$json.openrouter_body}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openRouterApi"
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "7f8a449b-1d98-4df3-abc2-aa1d145cf146",
      "name": "Parse Research Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1312,
        400
      ],
      "parameters": {
        "jsCode": "function parseJson(text) {\n  const cleaned = String(text || '').trim().replace(/^```json\\s*/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n  const first = cleaned.indexOf('{');\n  const last = cleaned.lastIndexOf('}');\n  const candidate = first >= 0 && last >= first ? cleaned.slice(first, last + 1) : cleaned;\n  return JSON.parse(candidate);\n}\nconst content = $json.choices?.[0]?.message?.content || '{}';\nconst parsed = parseJson(content);\nreturn [{ json: {\n  ...$('Build Research Prompt').item.json,\n  profile_markdown: parsed.profile_markdown,\n  slack_summary: parsed.slack_summary,\n  market_cap_summary: parsed.market_cap_summary,\n  risks_summary: parsed.risks_summary,\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "597e20a0-0628-4a4c-923d-6ede7669fe0c",
      "name": "Create Research Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1536,
        400
      ],
      "parameters": {
        "name": "={{$json.company_name.replace(/[\\\\/:*?\"<>|]/g, '-') + ' - research - ' + $now.toFormat('yyyy-LL-dd')}}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "url",
          "value": "=https://drive.google.com/drive/u/0/folders/1GH-YouAAImKugZ11IbqA6Ouw8B9U17-I"
        },
        "resource": "folder"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "662c9eb5-a21b-4d56-98fd-c508cb82cfa2",
      "name": "Create Profile Doc",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        1760,
        400
      ],
      "parameters": {
        "title": "={{$node[\"Parse Research Output\"].json.company_name}} Company Profile",
        "folderId": "={{$json.id}}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "26a8430d-4b11-4af8-a1d4-28c456e23ccd",
      "name": "Update Profile Doc",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        1984,
        400
      ],
      "parameters": {
        "actionsUi": {
          "actionFields": [
            {
              "text": "={{$node[\"Parse Research Output\"].json.profile_markdown}}",
              "action": "insert"
            }
          ]
        },
        "operation": "update",
        "documentURL": "={{ $json.id }}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7c808348-0c4f-468c-98c7-227cdddf254d",
      "name": "Upsert Company Record",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2208,
        400
      ],
      "parameters": {
        "query": "\nINSERT INTO aiemp_research_analyst_companies (\n  company_name,\n  sector,\n  companies_house_number,\n  registered_address,\n  incorporation_date,\n  sic_codes,\n  directors,\n  market_cap,\n  stock_ticker,\n  website_url,\n  profile_doc_url,\n  last_researched_at\n)\nVALUES (\n  '{{ $node[\"Parse Research Output\"].json.company_name.replace(/'/g, \"''\") }}',\n  '{{ ($node[\"Parse Research Output\"].json.sector || '').replace(/'/g, \"''\") }}',\n  '{{ $node[\"Parse Research Output\"].json.company_number.replace(/'/g, \"''\") }}',\n  '{{ JSON.stringify($node[\"Build Research Bundle\"].json.sources.profile?.registered_office_address || {}).replace(/'/g, \"''\") }}',\n  {{ $node[\"Build Research Bundle\"].json.sources.profile?.date_of_creation ? \"'\" + $node[\"Build Research Bundle\"].json.sources.profile.date_of_creation + \"'\" : 'NULL' }},\n  ARRAY[{{ (($node[\"Build Research Bundle\"].json.sources.profile?.sic_codes || []).map((code) => \"'\" + String(code).replace(/'/g, \"''\") + \"'\").join(',')) || '' }}],\n  '{{ JSON.stringify(($node[\"Build Research Bundle\"].json.sources.officers?.items || []).slice(0, 8).map((officer) => ({ name: officer.name, role: officer.officer_role, appointed_on: officer.appointed_on }))).replace(/'/g, \"''\") }}'::jsonb,\n  {{ $node[\"Build Research Bundle\"].json.market_cap ? $node[\"Build Research Bundle\"].json.market_cap : 'NULL' }},\n  {{ $node[\"Parse Research Output\"].json.ticker ? \"'\" + $node[\"Parse Research Output\"].json.ticker.replace(/'/g, \"''\") + \"'\" : 'NULL' }},\n  {{ $node[\"Build Research Bundle\"].json.website_url ? \"'\" + $node[\"Build Research Bundle\"].json.website_url.replace(/'/g, \"''\") + \"'\" : 'NULL' }},\n  '{{ (\"https://docs.google.com/document/d/\" + $node[\"Create Profile Doc\"].json.id + \"/edit\").replace(/'/g, \"''\") }}',\n  NOW()\n)\nON CONFLICT (companies_house_number) DO UPDATE SET\n  company_name = EXCLUDED.company_name,\n  sector = EXCLUDED.sector,\n  registered_address = EXCLUDED.registered_address,\n  incorporation_date = EXCLUDED.incorporation_date,\n  sic_codes = EXCLUDED.sic_codes,\n  directors = EXCLUDED.directors,\n  market_cap = EXCLUDED.market_cap,\n  stock_ticker = EXCLUDED.stock_ticker,\n  website_url = EXCLUDED.website_url,\n  profile_doc_url = EXCLUDED.profile_doc_url,\n  last_researched_at = NOW();\nSELECT id, company_name, sector, companies_house_number, profile_doc_url, pitch_deck_url\nFROM aiemp_research_analyst_companies\nWHERE companies_house_number = '{{ $node[\"Parse Research Output\"].json.company_number.replace(/'/g, \"''\") }}'\nLIMIT 1;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "c4c8e333-b238-4fdd-89a2-648aafdeb909",
      "name": "Save Research Memory",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2432,
        400
      ],
      "parameters": {
        "query": "\nINSERT INTO aiemp_research_analyst_research_memory (company_id, intent, content_text, embedding)\nVALUES (\n  '{{ $node[\"Upsert Company Record\"].json.id }}',\n  'research',\n  '{{ $node[\"Parse Research Output\"].json.profile_markdown.replace(/'/g, \"''\") }}',\n  NULL\n);\nSELECT '{{ $node[\"Upsert Company Record\"].json.id }}' AS company_id;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "d5351e91-031f-4477-bedc-5374816a3c15",
      "name": "Update Client DB Research",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2656,
        400
      ],
      "parameters": {
        "columns": {
          "value": {
            "sector": "={{$node[\"Parse Research Output\"].json.sector}}",
            "market_cap": "={{$node[\"Build Research Bundle\"].json.market_cap || \"\"}}",
            "company_name": "={{$node[\"Parse Research Output\"].json.company_name}}",
            "pitch_deck_url": "={{$node[\"Upsert Company Record\"].json.pitch_deck_url || \"\"}}",
            "last_researched": "={{$now.toISO()}}",
            "profile_doc_url": "={{\"https://docs.google.com/document/d/\" + $node[\"Create Profile Doc\"].json.id + \"/edit\"}}",
            "companies_house_number": "={{$node[\"Parse Research Output\"].json.company_number}}"
          },
          "schema": [
            {
              "id": "company_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "company_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sector",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "sector",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "companies_house_number",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "companies_house_number",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "market_cap",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "market_cap",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_researched",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_researched",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profile_doc_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "profile_doc_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "pitch_deck_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "pitch_deck_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "companies_house_number"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "=https://docs.google.com/spreadsheets/d/1yJ-UKOEUqIruCv-IBA33oN4taQTB1TA8M4a6D2iSiOU/edit?gid=0#gid=0"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2cbd75bd-4b69-4da2-bc4f-354fb6890b02",
      "name": "Research Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        400
      ],
      "parameters": {
        "jsCode": "const docUrl = `https://docs.google.com/document/d/${$('Create Profile Doc').first().json.id}/edit`;\nreturn [{ json: {\n  channel: $('Parse Research Output').first().json.channel,\n  thread_ts: $('Parse Research Output').first().json.thread_ts,\n  reply_text: `Research complete for *${$('Parse Research Output').first().json.company_name}*\\n\\n${$('Parse Research Output').first().json.slack_summary}\\n\\nProfile doc: ${docUrl}`\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "93e31fc7-fff6-4cd9-85fe-c999006bbc29",
      "name": "Has Pitch Company?",
      "type": "n8n-nodes-base.if",
      "position": [
        -480,
        1968
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.company_name }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2486f00c-8c80-4494-84b5-d622ff20eabb",
      "name": "Pitch Missing Input Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        2176
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $json.channel,\n  thread_ts: $json.thread_ts,\n  reply_text: 'Pitch requests need a company name. Example: `Prepare pitch for Revolut`.'\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "ee986493-5763-4050-91ca-29b84c8edb8b",
      "name": "Load Company Record",
      "type": "n8n-nodes-base.postgres",
      "position": [
        -256,
        1872
      ],
      "parameters": {
        "query": "\nSELECT * FROM (\n  SELECT\n    c.id,\n    c.company_name,\n    c.sector,\n    c.companies_house_number,\n    c.profile_doc_url,\n    c.pitch_deck_url,\n    c.website_url,\n    c.stock_ticker,\n    c.last_researched_at\n  FROM aiemp_research_analyst_companies c\n  WHERE lower(c.company_name) = lower('{{ $json.company_name.replace(/'/g, \"''\") }}')\n  ORDER BY c.created_at DESC\n  LIMIT 1\n) found\nUNION ALL\nSELECT\n  NULL::uuid,\n  NULL::text,\n  NULL::text,\n  NULL::text,\n  NULL::text,\n  NULL::text,\n  NULL::text,\n  NULL::text,\n  NULL::timestamptz\nWHERE NOT EXISTS (\n  SELECT 1 FROM aiemp_research_analyst_companies c\n  WHERE lower(c.company_name) = lower('{{ $json.company_name.replace(/'/g, \"''\") }}')\n);\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "58d18b5b-538c-4e26-8d01-1efab45587c7",
      "name": "Has Prior Research?",
      "type": "n8n-nodes-base.if",
      "position": [
        -32,
        1872
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "d521abb4-616d-451c-8669-34ab4f06d938",
      "name": "Pitch Missing Research Response",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        1776
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $('Parse Intent').item.json.channel,\n  thread_ts: $('Parse Intent').item.json.thread_ts,\n  reply_text: `No stored research exists for *${$('Parse Intent').item.json.company_name}*. Run a research request first.`\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "6b8693aa-e5fd-4381-94f2-b3703a383a61",
      "name": "Load Research Memory",
      "type": "n8n-nodes-base.postgres",
      "position": [
        192,
        1936
      ],
      "parameters": {
        "query": "\nSELECT * FROM (\n  SELECT rm.content_text, rm.created_at\n  FROM aiemp_research_analyst_research_memory rm\n  WHERE rm.company_id = '{{ $node[\"Load Company Record\"].json.id }}'\n    AND rm.intent = 'research'\n  ORDER BY rm.created_at DESC\n  LIMIT 1\n) found\nUNION ALL\nSELECT NULL::text, NULL::timestamptz\nWHERE NOT EXISTS (\n  SELECT 1\n  FROM aiemp_research_analyst_research_memory rm\n  WHERE rm.company_id = '{{ $node[\"Load Company Record\"].json.id }}'\n    AND rm.intent = 'research'\n);\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "e67a1161-1a48-4782-8c28-df474380cad8",
      "name": "Has Research Memory?",
      "type": "n8n-nodes-base.if",
      "position": [
        416,
        1936
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.content_text }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "6555fbbf-de55-4ad6-a959-6a1a06953bc5",
      "name": "Create Pitch Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        640,
        1984
      ],
      "parameters": {
        "name": "={{$node['Load Company Record'].json.company_name.replace(/[\\\\/:*?\"<>|]/g, '-') + ' - pitch - ' + $now.toFormat('yyyy-LL-dd')}}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "url",
          "value": "=https://drive.google.com/drive/u/0/folders/1GH-YouAAImKugZ11IbqA6Ouw8B9U17-I"
        },
        "resource": "folder"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "73cecdcf-4341-4199-9d85-8369d058df27",
      "name": "Build Pitch Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        1984
      ],
      "parameters": {
        "jsCode": "const company = $('Load Company Record').item.json;\nconst memory = $('Load Research Memory').item.json.content_text;\nconst body = {\n  model: 'anthropic/claude-haiku-4.5',\n  temperature: 0.2,\n  messages: [\n    {\n      role: 'system',\n      content: [\n        'You prepare pitch materials for a boutique corporate finance team.',\n        'Free-tier mode only: do not invent deal comps, valuation multiples, or proprietary transaction data.',\n        'If comps are unavailable, say so plainly.',\n        'All slide content must be short, presentation-ready, and formatted as concise bullet points.',\n        'Do not write narrative paragraphs for slide fields.',\n        'For profile_summary, financials, opportunity_summary, and comps_note, return 3 to 5 bullets max per field.',\n        'Keep each bullet to one short line where possible.',\n        'If the template contains charts, leave chart content implied by the bullets rather than describing charts in prose.',\n        'Return raw JSON only.'\n      ].join(' '),\n    },\n    {\n      role: 'user',\n      content: `Create pitch materials for ${company.company_name}. Stored research:\\n${memory}\\n\\nImportant: the deck is copied into a slide template. Keep the slide text compact so it fits neatly into text boxes. Use bullet points, not paragraphs.` ,\n    },\n  ],\n  response_format: {\n    type: 'json_schema',\n    json_schema: {\n      name: 'finance_pitch_output',\n      strict: true,\n      schema: {\n        type: 'object',\n        additionalProperties: false,\n        properties: {\n          profile_summary: { type: 'string' },\n          financials: { type: 'string' },\n          opportunity_summary: { type: 'string' },\n          comps_note: { type: 'string' },\n          press_release_markdown: { type: 'string' },\n          slack_summary: { type: 'string' }\n        },\n        required: ['profile_summary', 'financials', 'opportunity_summary', 'comps_note', 'press_release_markdown', 'slack_summary']\n      }\n    }\n  }\n};\nreturn [{ json: {\n  channel: $('Parse Intent').item.json.channel,\n  thread_ts: $('Parse Intent').item.json.thread_ts,\n  company_name: company.company_name,\n  sector: company.sector,\n  openrouter_body: body,\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "23ec349a-0a7e-4a8c-86af-07fbd651e9ca",
      "name": "Pitch AI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1088,
        1984
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{$json.openrouter_body}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openRouterApi"
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "e9a5bba7-06a6-4076-b3e3-092f6d095cfa",
      "name": "Parse Pitch Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1312,
        1984
      ],
      "parameters": {
        "jsCode": "function parseJson(text) {\n  const cleaned = String(text || '').trim().replace(/^```json\\s*/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n  const first = cleaned.indexOf('{');\n  const last = cleaned.lastIndexOf('}');\n  const candidate = first >= 0 && last >= first ? cleaned.slice(first, last + 1) : cleaned;\n  return JSON.parse(candidate);\n}\nconst content = $json.choices?.[0]?.message?.content || '{}';\nconst parsed = parseJson(content);\nreturn [{ json: {\n  ...$('Build Pitch Prompt').item.json,\n  profile_summary: parsed.profile_summary,\n  financials: parsed.financials,\n  opportunity_summary: parsed.opportunity_summary,\n  comps_note: parsed.comps_note,\n  press_release_markdown: parsed.press_release_markdown,\n  slack_summary: parsed.slack_summary,\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "4375166b-caa0-46ff-8dce-3468f2144bdd",
      "name": "Copy Slides Template",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1536,
        1984
      ],
      "parameters": {
        "name": "={{$node[\"Parse Pitch Output\"].json.company_name}} Pitch Deck",
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "1zQv_cbafHzd4JNsr711Rm0558O6W_S1SODc-u6tVS0k"
        },
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{$node[\"Create Pitch Folder\"].json.id}}"
        },
        "operation": "copy",
        "sameFolder": false
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "65b78c5e-1785-46e8-af6f-2fe0bb703209",
      "name": "Create Custom Presentation",
      "type": "n8n-nodes-base.googleSlides",
      "position": [
        1760,
        1984
      ],
      "parameters": {
        "textUi": {
          "textValues": [
            {
              "text": "{company_name}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.company_name}}"
            },
            {
              "text": "{sector}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.sector || \"\"}}"
            },
            {
              "text": "{profile_summary}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.profile_summary}}"
            },
            {
              "text": "{financials}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.financials}}"
            },
            {
              "text": "{opportunity_summary}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.opportunity_summary}}"
            },
            {
              "text": "{comps_note}",
              "replaceText": "={{$node[\"Parse Pitch Output\"].json.comps_note}}"
            }
          ]
        },
        "options": {},
        "operation": "replaceText",
        "presentationId": "={{$json.id}}"
      },
      "credentials": {
        "googleSlidesOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a3861a20-dc3c-4e7f-863e-4e906ba07535",
      "name": "Create Press Release Doc",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        1984,
        1984
      ],
      "parameters": {
        "title": "={{$node[\"Parse Pitch Output\"].json.company_name}} Press Release Draft",
        "folderId": "={{$node[\"Create Pitch Folder\"].json.id}}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4c54db00-6ed1-4588-9cdc-5040fbacbc87",
      "name": "Update Press Release Doc",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        2208,
        1984
      ],
      "parameters": {
        "actionsUi": {
          "actionFields": [
            {
              "text": "={{$node[\"Parse Pitch Output\"].json.press_release_markdown}}",
              "action": "insert"
            }
          ]
        },
        "operation": "update",
        "documentURL": "={{$json.id}}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d62fbbdd-2198-4854-ad1f-db083e94ad6e",
      "name": "Update Pitch Assets",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2432,
        1984
      ],
      "parameters": {
        "query": "\nUPDATE aiemp_research_analyst_companies\nSET pitch_deck_url = '{{ (\"https://docs.google.com/presentation/d/\" + $node[\"Copy Slides Template\"].json.id + \"/edit\").replace(/'/g, \"''\") }}'\nWHERE id = '{{ $node[\"Load Company Record\"].json.id }}';\nSELECT id, company_name, sector, companies_house_number, profile_doc_url, pitch_deck_url\nFROM aiemp_research_analyst_companies\nWHERE id = '{{ $node[\"Load Company Record\"].json.id }}'\nLIMIT 1;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "45ad1354-cba8-408f-a2a7-6d9cb743350c",
      "name": "Update Client DB Pitch",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2656,
        1984
      ],
      "parameters": {
        "columns": {
          "value": {
            "sector": "={{$node[\"Load Company Record\"].json.sector || \"\"}}",
            "market_cap": "",
            "company_name": "={{$node[\"Load Company Record\"].json.company_name}}",
            "pitch_deck_url": "={{\"https://docs.google.com/presentation/d/\" + $node[\"Copy Slides Template\"].json.id + \"/edit\"}}",
            "last_researched": "={{$node[\"Load Company Record\"].json.last_researched_at || \"\"}}",
            "profile_doc_url": "={{$node[\"Load Company Record\"].json.profile_doc_url || \"\"}}",
            "companies_house_number": "={{$node[\"Load Company Record\"].json.companies_house_number || \"\"}}"
          },
          "schema": [
            {
              "id": "company_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "company_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sector",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "sector",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "companies_house_number",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "companies_house_number",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "market_cap",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "market_cap",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_researched",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_researched",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profile_doc_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "profile_doc_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "pitch_deck_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "pitch_deck_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "companies_house_number"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "=https://docs.google.com/spreadsheets/d/1yJ-UKOEUqIruCv-IBA33oN4taQTB1TA8M4a6D2iSiOU/edit?gid=0#gid=0"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "bc1c8cab-bb0c-4bf0-b2fb-b58363817b61",
      "name": "Pitch Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        1984
      ],
      "parameters": {
        "jsCode": "const deckUrl = `https://docs.google.com/presentation/d/${$('Copy Slides Template').first().json.id}/edit`;\nconst docUrl = `https://docs.google.com/document/d/${$('Create Press Release Doc').first().json.id}/edit`;\nreturn [{ json: {\n  channel: $('Parse Pitch Output').first().json.channel,\n  thread_ts: $('Parse Pitch Output').first().json.thread_ts,\n  reply_text: `Pitch materials ready for *${$('Parse Pitch Output').first().json.company_name}*\\n\\n${$('Parse Pitch Output').first().json.slack_summary}\\n\\nPitch deck: ${deckUrl}\\nPress release draft: ${docUrl}`\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "32982ac8-cf47-4bf5-87b6-9e720f2b7959",
      "name": "Has Brief Sector?",
      "type": "n8n-nodes-base.if",
      "position": [
        -608,
        2496
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.sector }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "438c6f65-01fa-44cf-a850-69b107fc95d8",
      "name": "Brief Missing Sector Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        2560
      ],
      "parameters": {
        "jsCode": "return [{ json: {\n  channel: $json.channel,\n  thread_ts: $json.thread_ts,\n  reply_text: 'Industry briefings need a sector. Example: `Industry briefing UK fintech`.'\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "15612463-3983-470c-9dde-193dd813ebdd",
      "name": "Sector News",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -64,
        2288
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v2/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={{({ query: `${$json.sector} UK mergers acquisitions trends recent news`, sources: [\"web\", \"news\"], limit: 10, scrapeOptions: { onlyMainContent: true, formats: [\"markdown\"] } })}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "cfb909fd-3d75-4975-a638-76b246c41cdd",
      "name": "Wrap Sector News",
      "type": "n8n-nodes-base.code",
      "position": [
        288,
        2320
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'news', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "d3e37619-1e6b-48d7-b5c6-95dbb15c8058",
      "name": "ONS M&A Source",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -128,
        2624
      ],
      "parameters": {
        "url": "https://www.ons.gov.uk/businessindustryandtrade/changestobusiness/mergersandacquisitions/bulletins/mergersandacquisitionsinvolvingukcompanies/julytoseptember2024/relateddata",
        "options": {}
      },
      "typeVersion": 4.4
    },
    {
      "id": "c2f28081-28a0-4262-af9a-22fb808473ff",
      "name": "Wrap ONS M&A",
      "type": "n8n-nodes-base.code",
      "position": [
        272,
        2640
      ],
      "parameters": {
        "jsCode": "return [{ json: { source: 'ons', data: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f2558a5e-85

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

How this works

This workflow turns Slack requests into polished UK M&A research and pitch materials by pulling company and market data, then generating structured outputs in Google Docs, Google Slides and Google Sheets. It suits boutique corporate finance teams that need fast, consistent deliverables without hiring extra junior analysts. The core step is the intent classifier that reads the Slack message and routes it to the right research or production path.

Use it for recurring briefing or deck requests that follow clear patterns, but avoid it for highly bespoke or time-sensitive deals where manual oversight is essential. Common variations include swapping the data sources for different sectors or adding approval steps before the final Google Drive files are shared.

About this workflow

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and produces structured outputs in Google Docs, Google Slides, Google Sheets, and…

Source: https://n8n.io/workflows/14762/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

This n8n workflow automates the transformation of spreadsheet data into professional charts and graphs using AI-driven analysis. Triggered via Slack, it processes uploaded files (Excel, CSV, Google Sh

Agent, Postgres, HTTP Request +8
Data & Sheets

AI Money Tracker Chatbot. Uses telegramTrigger, postgres, googleSheets, telegram. Event-driven trigger; 24 nodes.

Telegram Trigger, Postgres, Google Sheets +2
Data & Sheets

This n8n workflow retrieves AI agent chat memory logs stored in Postgres and pushes them to Google Sheets, creating one sheet per session. It’s useful for teams building chat-based products or agents

Postgres, HTTP Request, Google Sheets
Data & Sheets

This workflow automates the generation of AI-enhanced, contextualized images using FLUX Kontext, based on prompts stored in a Google Sheet. The generated images are then saved to Google Drive, and the

HTTP Request, Google Sheets, Google Drive
Data & Sheets

Author: Oliver Bardenheier

HTTP Request, Google Drive, Google Sheets