diff --git a/workflows/Build Gmail Contact Database with GPT-5 Nano, Brave Search & Google Sheets-10778/build_gmail_contact_database_with_gpt-5_nano_brave_search_google_sheets.json b/workflows/Build Gmail Contact Database with GPT-5 Nano, Brave Search & Google Sheets-10778/build_gmail_contact_database_with_gpt-5_nano_brave_search_google_sheets.json new file mode 100644 index 000000000..d66bf162e --- /dev/null +++ b/workflows/Build Gmail Contact Database with GPT-5 Nano, Brave Search & Google Sheets-10778/build_gmail_contact_database_with_gpt-5_nano_brave_search_google_sheets.json @@ -0,0 +1 @@ +{"id":"PkW9CD0aSJGNrzqd","meta":{"instanceId":"b1699e1d8ef82aaaaf2eed0ed67f215d7574a625e2d012a1bcd013054b0defdf","templateCredsSetupCompleted":true},"name":"CreateContactDB_TEMPLATE","tags":[],"nodes":[{"id":"6496a003-58aa-48ee-aab6-fe3f66518070","name":"When clicking ‘Execute workflow’","type":"n8n-nodes-base.manualTrigger","position":[-2016,-144],"parameters":{},"typeVersion":1},{"id":"c01e946f-a80f-438c-b967-07b466dd9ab9","name":"Loop Over Items","type":"n8n-nodes-base.splitInBatches","position":[-1312,-144],"parameters":{"options":{"reset":false}},"typeVersion":3},{"id":"6cb6cd5f-6b12-4881-bdbc-22618814284a","name":"Remove Duplicates","type":"n8n-nodes-base.removeDuplicates","position":[-1504,-144],"parameters":{"compare":"selectedFields","options":{},"fieldsToCompare":"To"},"typeVersion":2},{"id":"5c1f7978-d638-42c6-9a93-f4c3e49189ae","name":"No Operation, do nothing","type":"n8n-nodes-base.noOp","position":[-1312,-480],"parameters":{},"typeVersion":1},{"id":"71db1984-814a-4cda-a3c8-6550a50ae094","name":"3s","type":"n8n-nodes-base.wait","position":[-1120,-80],"webhookId":"419094fc-8654-49d6-8455-0355b5ef5bc9","parameters":{"amount":3},"typeVersion":1.1},{"id":"9da7b0fd-57f4-44d9-87d9-61087d660b0f","name":"GetHTML","type":"n8n-nodes-base.httpRequest","position":[480,32],"parameters":{"url":"={{ $json.url }}","options":{"timeout":10000,"allowUnauthorizedCerts":true}},"typeVersion":4.3},{"id":"39fdafbb-2669-4dbe-b30d-596d960e5338","name":"MailParser","type":"n8n-nodes-base.code","position":[-624,336],"parameters":{"jsCode":"const startData = $('Loop Over Items').first().json;\n\n// ====== HELPER FUNCTIONS ======\nfunction extractEmailParts(toField) {\n const regex = /\"?([^\"<]*)\"?\\s*<([^>]+)>/;\n const match = toField.match(regex);\n\n if (match) {\n return {\n name: match[1]?.trim() || null,\n email: match[2]?.trim() || null\n };\n } else if (toField.includes('@')) {\n return {\n name: null,\n email: toField.trim()\n };\n } else {\n return {\n name: null,\n email: null\n };\n }\n}\n\nfunction deriveWebsiteFromEmail(email) {\n if (!email) return null;\n const domain = email.split('@')[1]?.toLowerCase();\n if (!domain) return null;\n\n // seznam běžných free mail domén, které nechceme použít jako web\n const excludedDomains = [\n 'gmail.com', 'outlook.com', 'hotmail.com', 'seznam.cz',\n 'email.cz', 'post.cz', 'yahoo.com', 'icloud.com',\n 'centrum.cz', 'zoho.com', 'protonmail.com'\n ];\n\n if (excludedDomains.includes(domain)) return null;\n\n // pokud je doména firemní, vygeneruj webovou adresu\n return `https://${domain.replace(/^www\\./, '')}`;\n}\n\n// ====== MAIN PROCESS ======\nconst items = Array.isArray(startData) ? startData : [startData];\n\nreturn items.map(item => {\n const input = item.To || \"\";\n const { name, email } = extractEmailParts(input);\n const website = deriveWebsiteFromEmail(email);\n\n return {\n json: {\n name,\n email,\n phone: null,\n website,\n facebook: null,\n instagram: null,\n spotify: null,\n youtube: null,\n linkedin: null,\n twitter: null,\n linktree: null,\n tiktok: null,\n soundcloud: null,\n bandcamp: null\n }\n };\n});\n"},"typeVersion":2},{"id":"90310a89-c904-4f0b-8459-a8936289b573","name":"HTMLParser","type":"n8n-nodes-base.code","position":[848,-64],"parameters":{"jsCode":"const mailParserData = $('MailParser').item.json;\n\n// Social media patterns (rozšířené)\nconst socialPatterns = {\n facebook: /(?:https?:\\/\\/)?(?:www\\.|m\\.|mobile\\.)?(?:facebook\\.com|fb\\.com|fb\\.me)\\/[a-zA-Z0-9._-]+/gi,\n instagram: /(?:https?:\\/\\/)?(?:www\\.)?instagram\\.com\\/[a-zA-Z0-9._-]+/gi,\n twitter: /(?:https?:\\/\\/)?(?:www\\.)?(?:twitter\\.com|x\\.com)\\/[a-zA-Z0-9._-]+/gi,\n linkedin: /(?:https?:\\/\\/)?(?:www\\.)?linkedin\\.com\\/(?:company|in)\\/[a-zA-Z0-9._-]+/gi,\n youtube: /(?:https?:\\/\\/)?(?:www\\.)?youtube\\.com\\/(?:channel|c|user|@)\\/[a-zA-Z0-9._-]+/gi,\n tiktok: /(?:https?:\\/\\/)?(?:www\\.)?tiktok\\.com\\/@[a-zA-Z0-9._-]+/gi,\n spotify: /(?:https?:\\/\\/)?(?:www\\.)?(?:open\\.)?spotify\\.com\\/(?:artist|user)\\/[a-zA-Z0-9._-]+/gi,\n soundcloud: /(?:https?:\\/\\/)?(?:www\\.)?soundcloud\\.com\\/[a-zA-Z0-9._-]+/gi,\n bandcamp: /(?:https?:\\/\\/)?[a-zA-Z0-9._-]+\\.bandcamp\\.com/gi,\n linktree: /(?:https?:\\/\\/)?(?:www\\.)?linktr\\.ee\\/[a-zA-Z0-9._-]+/gi\n};\n\n// Funkce pro čištění URL\nfunction cleanUrl(url) {\n return url.split('?')[0].split('#')[0].replace(/\\/$/, '');\n}\n\n// Funkce pro extrakci odkazů z HTML stringu\nfunction extractSocialLinks(html) {\n if (typeof html !== 'string') {\n if (Buffer.isBuffer(html)) {\n html = html.toString('utf-8');\n } else if (typeof html === 'object') {\n html = JSON.stringify(html);\n } else {\n html = String(html);\n }\n }\n \n const links = {};\n for (const [platform, pattern] of Object.entries(socialPatterns)) {\n const matches = html.match(pattern);\n if (matches) {\n const uniqueUrls = [...new Set(matches.map(cleanUrl))];\n links[platform] = uniqueUrls[0];\n }\n }\n return links;\n}\n\n// Projdi všechny agregované položky a sbírej social links\nconst allSocialLinks = {};\n\n// Pokud máš agregátor, data jsou v poli\nconst items = $input.item.json.data || [$input.item.json];\n\nfor (const item of items) {\n const html = item.data || item.body || item;\n const socialLinks = extractSocialLinks(html);\n \n // Merge výsledků - vezmi první nenulovou hodnotu pro každou platformu\n for (const [platform, url] of Object.entries(socialLinks)) {\n if (url && !allSocialLinks[platform]) {\n allSocialLinks[platform] = url;\n }\n }\n}\n\n// Vrať výsledek ve správné struktuře\nreturn [{\n json: {\n name: mailParserData.name || null,\n email: mailParserData.email || null,\n phone: mailParserData.phone || null,\n website: mailParserData.website || null,\n facebook: allSocialLinks.facebook || null,\n instagram: allSocialLinks.instagram || null,\n spotify: allSocialLinks.spotify || null,\n youtube: allSocialLinks.youtube || null,\n linkedin: allSocialLinks.linkedin || null,\n twitter: allSocialLinks.twitter || null,\n linktree: allSocialLinks.linktree || null,\n tiktok: allSocialLinks.tiktok || null,\n soundcloud: allSocialLinks.soundcloud || null,\n bandcamp: allSocialLinks.bandcamp || null\n }\n}];"},"typeVersion":2},{"id":"37e7b272-b040-48ad-988c-fae2d699124b","name":"SearchWebsite","type":"@brave/n8n-nodes-brave-search.braveSearch","position":[-288,48],"parameters":{"count":3,"query":"={{ $json.website }}","additionalParameters":{"safesearch":"moderate","spellcheck":true,"search_lang":"en","result_filter":["web"]}},"credentials":{"braveSearchApi":{"id":"AuLLZRyeyPzeuRMi","name":"TEMPLATE"}},"typeVersion":1},{"id":"c2be9ffd-5e4a-4973-ba62-2fed3d3814cd","name":"Loop Over Items1","type":"n8n-nodes-base.splitInBatches","position":[272,16],"parameters":{"options":{"reset":false}},"typeVersion":3},{"id":"1030fb06-7742-4f91-9181-6ce03192be9e","name":"No Operation, do nothing1","type":"n8n-nodes-base.noOp","position":[-112,352],"parameters":{},"typeVersion":1},{"id":"016004af-0c29-42ab-b4c7-00d5b0e41939","name":"InboxMessages?","type":"n8n-nodes-base.if","position":[-784,-80],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"7a59930e-ba0f-42a6-996f-02146e10d5bb","operator":{"type":"number","operation":"gt"},"leftValue":"={{ Object.keys($items(\"GetInboxMessages\")[0].json).length }}","rightValue":0}]}},"typeVersion":2.2},{"id":"94f0a766-e322-4c48-bfb1-f0bb1e720b16","name":"ValidDomain?","type":"n8n-nodes-base.if","position":[-448,336],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"1e8aeeb1-4f92-482c-a070-2da6a5ebb847","operator":{"type":"string","operation":"notEmpty","singleValue":true},"leftValue":"={{ $json.website }}","rightValue":""}]}},"typeVersion":2.2,"alwaysOutputData":false},{"id":"7e1e609c-f4f1-4513-a408-a28de29939bc","name":"GetInboxMessages","type":"n8n-nodes-base.gmail","position":[-944,-80],"webhookId":"db1c75f3-4d7a-49c1-96a9-7d3c9e42f624","parameters":{"limit":1,"simple":false,"filters":{"sender":"={{ $json.To }}"},"options":{},"operation":"getAll"},"credentials":{"gmailOAuth2":{"id":"UvhyTD0WB1lCZDX1","name":"TEMPLATE"}},"typeVersion":2.1,"alwaysOutputData":true},{"id":"5d6ace50-de7f-46f0-95c6-6bdfd37c387f","name":"SelectTo&CC","type":"n8n-nodes-base.set","position":[-1664,-144],"parameters":{"include":"selected","options":{},"assignments":{"assignments":[{"id":"f7b7e3b7-9f60-48e7-9158-972068caae11","name":"","type":"string","value":""}]},"includeFields":"To, Cc","includeOtherFields":true},"typeVersion":3.4},{"id":"c410c068-c1ba-4624-83e4-da282f05aa1d","name":"LoadSentMessages","type":"n8n-nodes-base.gmail","position":[-1840,-144],"webhookId":"6d61edf0-07e0-42a8-bb68-af95ade93b3d","parameters":{"filters":{"labelIds":["SENT"]},"operation":"getAll","returnAll":true},"credentials":{"gmailOAuth2":{"id":"UvhyTD0WB1lCZDX1","name":"TEMPLATE"}},"retryOnFail":true,"typeVersion":2.1,"waitBetweenTries":5000},{"id":"31188212-380d-4aeb-8746-ff568e2a939e","name":"WriteToDB","type":"n8n-nodes-base.googleSheets","position":[1072,368],"parameters":{"columns":{"value":{},"schema":[{"id":"name","type":"string","display":true,"removed":false,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email","type":"string","display":true,"removed":false,"required":false,"displayName":"email","defaultMatch":false,"canBeUsedToMatch":true},{"id":"phone","type":"string","display":true,"removed":false,"required":false,"displayName":"phone","defaultMatch":false,"canBeUsedToMatch":true},{"id":"website","type":"string","display":true,"removed":false,"required":false,"displayName":"website","defaultMatch":false,"canBeUsedToMatch":true},{"id":"facebook","type":"string","display":true,"removed":false,"required":false,"displayName":"facebook","defaultMatch":false,"canBeUsedToMatch":true},{"id":"instagram","type":"string","display":true,"removed":false,"required":false,"displayName":"instagram","defaultMatch":false,"canBeUsedToMatch":true},{"id":"spotify","type":"string","display":true,"removed":false,"required":false,"displayName":"spotify","defaultMatch":false,"canBeUsedToMatch":true},{"id":"youtube","type":"string","display":true,"removed":false,"required":false,"displayName":"youtube","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin","type":"string","display":true,"removed":false,"required":false,"displayName":"linkedin","defaultMatch":false,"canBeUsedToMatch":true},{"id":"twitter","type":"string","display":true,"removed":false,"required":false,"displayName":"twitter","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linktree","type":"string","display":true,"removed":false,"required":false,"displayName":"linktree","defaultMatch":false,"canBeUsedToMatch":true},{"id":"tiktok","type":"string","display":true,"removed":false,"required":false,"displayName":"tiktok","defaultMatch":false,"canBeUsedToMatch":true},{"id":"soundcloud","type":"string","display":true,"removed":false,"required":false,"displayName":"soundcloud","defaultMatch":false,"canBeUsedToMatch":true},{"id":"bandcamp","type":"string","display":true,"removed":false,"required":false,"displayName":"bandcamp","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"autoMapInputData","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1Mp7TkL2kNTsViCJCZc9D6zl6QlQ09s-3VmCreAWbRPk/edit#gid=0","cachedResultName":"List 1"},"documentId":{"__rl":true,"mode":"list","value":"1Mp7TkL2kNTsViCJCZc9D6zl6QlQ09s-3VmCreAWbRPk","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1Mp7TkL2kNTsViCJCZc9D6zl6QlQ09s-3VmCreAWbRPk/edit?usp=drivesdk","cachedResultName":"ContactsDB"}},"credentials":{"googleSheetsOAuth2Api":{"id":"h298dUyh0K1tO9s9","name":"TEMPLATE"}},"typeVersion":4.7},{"id":"cfdc0dc5-f981-408f-895e-83807d8779fd","name":"SelectImportantFields","type":"n8n-nodes-base.set","position":[-560,-416],"parameters":{"options":{},"assignments":{"assignments":[{"id":"c5a06fc6-9352-4540-aaf4-167d7771dcfe","name":"address","type":"string","value":"={{ $json.from.value[0].address }}"},{"id":"2fc70be2-0563-42d7-95ed-b3cb6d2d7adf","name":"name","type":"string","value":"={{ $json.from.value[0].name }}"},{"id":"8b57343e-8ec3-466d-b535-9994ec7c98f4","name":"text","type":"string","value":"={{ $json.text }}"},{"id":"d4b99119-6a0d-47f6-93d7-ffefd9815f95","name":"recipient name","type":"string","value":"={{ $json.to.value[0].name }}"},{"id":"1d5c2b97-527a-43be-8b02-d44d3d9c324a","name":"recipient address","type":"string","value":"={{ $json.to.value[0].address }}"}]}},"typeVersion":3.4},{"id":"1d80e46a-3af8-4d90-bdd5-b04dac6d0e94","name":"Information Extractor","type":"@n8n/n8n-nodes-langchain.informationExtractor","position":[-352,-416],"parameters":{"text":"=For contact:{{ $json.name }} with email : {{ $json.address }} extract required links from the text below:\n{{ $json.text }}\n\nRecipient to ignore: {{ $json[\"recipient name\"] }} with mail: {{ $json[\"recipient address\"] }}","options":{"systemPromptTemplate":"You are a precise data extraction model.\nYour task is to extract structured contact and social information from the provided email text. \nThe email may include Czech or English content, signatures, and quoted messages.\n---\n### INPUT:\nRaw text content of an email thread. It may contain multiple replies or forwarded messages.\nYou will also receive information about the recipient whose contacts should be IGNORED.\n---\n### EXTRACTION GOAL:\nExtract only contact and link information related to the **most recent sender** (ignore older quoted replies). \nDo not include any data from other participants in the thread.\n**CRITICAL: You must completely ignore and exclude any contact information, social links, or web addresses belonging to the specified recipient.** Even if the recipient's contacts appear in the email, they must NOT be extracted.\n---\n### RULES:\n- `tel`: prefer mobile numbers over landlines; normalize to digits only (remove \"+\", spaces, dashes). \n Example: \"+420 745 145 455\" → \"420745145455\".\n- `web`: the sender's primary website (e.g. domain like `https://bandname.com/`).\n- Social links (`facebook`, `instagram`, etc.): extract only if they clearly belong to the same entity as the sender. \n If uncertain, leave as `null`.\n- **IGNORE all contacts/socials/web belonging to the recipient specified in the input.**\n- Return `null` for any missing or unclear value.\n- Never fabricate or infer missing data.\n- Do not return `name` or `email` — those are handled elsewhere.\n- Ensure output is **valid JSON** and contains **all expected keys**.\n- No text, explanation, or formatting outside the JSON.\n---\n### OUTPUT:\nReturn exactly one valid JSON object following the schema above."},"schemaType":"manual","inputSchema":"{\n \"type\": \"object\",\n \"properties\": {\n \"tel\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Mobile or phone number, digits only without + or spaces.\"\n },\n \"web\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Primary website of the sender.\"\n },\n \"facebook\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to Facebook page or profile.\"\n },\n \"instagram\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to Instagram profile.\"\n },\n \"spotify\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to Spotify artist or profile.\"\n },\n \"youtube\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to YouTube channel.\"\n },\n \"linktree\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to Linktree page.\"\n },\n \"soundcloud\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to SoundCloud profile.\"\n },\n \"bandcamp\": {\n \"type\": [\"string\", \"null\"],\n \"description\": \"Link to Bandcamp page.\"\n }\n },\n \"required\": [\n \"tel\",\n \"web\",\n \"facebook\",\n \"instagram\",\n \"spotify\",\n \"youtube\",\n \"linktree\",\n \"apple_music\",\n \"soundcloud\",\n \"bandcamp\"\n ],\n \"additionalProperties\": false\n}"},"typeVersion":1.2},{"id":"e4aca200-2256-4de4-8a73-2474d30cff27","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[-432,-256],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-5-nano","cachedResultName":"gpt-5-nano"},"options":{}},"credentials":{"openAiApi":{"id":"SejrVHsogrtvT4yC","name":"TEMPLATE"}},"typeVersion":1.2},{"id":"9d8cbfad-b81c-498f-b80f-826061ffda22","name":"Merge","type":"n8n-nodes-base.code","position":[848,-224],"parameters":{"jsCode":"const Output = $input.first().json;\nconst OutputFromLLM = Output['output'];\nconst OutputFromMail = $('SelectImportantFields').item.json;\n\nreturn [\n {\n json: {\n name: OutputFromMail['name'],\n email: OutputFromMail['address'],\n phone: OutputFromLLM['tel'],\n website: OutputFromLLM['web'],\n facebook: OutputFromLLM['facebook'],\n instagram: OutputFromLLM['instagram'],\n spotify: OutputFromLLM['spotify'], \n youtube: OutputFromLLM['youtube'],\n linktree: OutputFromLLM['linktree'],\n apple_music: OutputFromLLM['apple_music'],\n soundcloud: OutputFromLLM['soundcloud'],\n bandcamp: OutputFromLLM['bandcamp']\n }\n }\n];\n"},"typeVersion":2},{"id":"9bc210e5-7292-490c-a06c-b60787c847f0","name":"HaveClusters","type":"n8n-nodes-base.if","position":[32,48],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"610b9311-dac5-4138-b378-77d410b8d073","operator":{"type":"number","operation":"gt"},"leftValue":"={{ Object.keys($items(\"GetClusters1\")[0].json).length }}","rightValue":0}]}},"typeVersion":2.2},{"id":"f06aa3f9-3814-4c0e-864f-715930ed4c54","name":"ReturnContactFromMailParser","type":"n8n-nodes-base.code","position":[160,256],"parameters":{"jsCode":"const Output = $('MailParser').first().json;\n\nreturn [\n {\n json: {\n ...Output\n }\n }\n] "},"typeVersion":2},{"id":"8075ed4e-2c33-42dc-a043-f8a26497ed1c","name":"GetClusters1","type":"n8n-nodes-base.code","position":[-128,48],"parameters":{"jsCode":"const results = $input.item.json.web.results;\nconst contactKeywords = ['kontakt','kontakty','info', 'informace','o-nas','o nas','kontaktni','spojeni', 'reach', 'dotaz', 'contact','contacts','about','about-us', 'info','information','reach-us','get-in-touch', 'connect','impressum','social','socialni','follow', 'sledujte','footer', 'pata'];\n\n\nconst clusterUrls = results\n .filter(r => r.cluster && r.cluster.length > 0)\n .flatMap(r => r.cluster.filter(c => {\n const titleLower = c.title.toLowerCase();\n const urlLower = c.url.toLowerCase();\n \n // Vrať true pokud title nebo URL obsahuje nějaké klíčové slovo\n return contactKeywords.some(keyword => \n titleLower.includes(keyword) || urlLower.includes(keyword)\n );\n }))\n .map(c => ({ url: c.url, title: c.title }));\n\nreturn clusterUrls.map(item => ({ json: item }));"},"typeVersion":2,"alwaysOutputData":true},{"id":"a4010343-622e-4fe1-8ead-ab6c0f4d0fc4","name":"Aggregate","type":"n8n-nodes-base.aggregate","position":[656,32],"parameters":{"options":{},"aggregate":"aggregateAllItemData"},"typeVersion":1},{"id":"2ed4d613-42e9-48c2-9e9c-fcc4c931ee98","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-2048,-304],"parameters":{"color":5,"width":672,"height":432,"content":"## Load & Filter Sent Emails\nTriggers workflow manually and loads all sent emails from Gmail.\n\nExtracts basic contact info (name, email) for processing."},"typeVersion":1},{"id":"a2b6ae22-af1f-4c73-a06e-2d5229ac7b7d","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1024,-144],"parameters":{"color":5,"width":336,"height":416,"content":"## Merge\nfrom AI branch combines extracted data from AI with information obtained from email (name, title)\n\n## HTML Parser\n\nThe HTML parser goes through the clusters it gets and tries to pull out contact info and social networks from the HTML content.\n\n## WriteToDB\n\nRecord is saved to GoogleSheets"},"typeVersion":1},{"id":"d5e9e02f-9f68-44b1-9aa9-4898c2366483","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-304,-96],"parameters":{"color":5,"width":1120,"height":320,"content":"## Web Search (No Email History)\nFor contacts without email history: extracts domain from email → validates it's not generic (gmail.com, etc.) → searches Brave API for contact pages → scrapes HTML"},"typeVersion":1},{"id":"5b809cf6-fe1e-455b-ad02-7214123c14a2","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-624,-576],"parameters":{"color":5,"width":560,"height":432,"content":"## AI Extraction from Email History\n\"For each contact, searches Gmail for existing email threads. If conversation exists, sends full thread to GPT-5 Nano to extract: phone numbers, social media profiles, websites, and other contact details.\""},"typeVersion":1},{"id":"d9a75660-6028-41f5-b2e8-f2e85a46ceb2","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[-2432,-512],"parameters":{"width":352,"height":640,"content":"## How it works\nThis workflow processes your Gmail sent folder to create an enriched contact database in Google Sheets. It uses a two-path approach:\n\nPath A : For contacts with email history, it searches Gmail threads and uses GPT-5 Nano to extract phone numbers, social media profiles, and websites from email signatures and message content.\n\nPath B (Web Search): For contacts without email history, it extracts their domain, validates it's not a generic provider (f.e. gmail.com), then uses Brave Search API to find their website's contact page. And HTML parser extracts details from the result.\n\n## Setup steps\n1. Create a Google Sheet using this template structure: [Make a copy here](https://docs.google.com/spreadsheets/d/1ox0cP_v8UuonAFr3eXkOFRlBb_P86NRedEaK4fss5cA/edit?usp=sharing)\n2. Connect credentials: Gmail, OpenAI API, Brave Search API, Google Sheets\n3. Update node \"SelectTabCC\" with your Sheet ID\n4. Customize excluded domains in \"ValidDomain?\" if needed\n5. Run manually to process all sent emails"},"typeVersion":1},{"id":"cd90fc19-4899-4c4f-9a65-252a5690494e","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[-1328,-336],"parameters":{"color":5,"width":672,"height":464,"content":"## GetInboxMessages\nSearches Gmail for last existing conversations with this contact.\n\nWhat it pulls:\n- Entire email threads (not just one message)\n- Includes: body, signatures, quoted replies\n- Both sent AND received messages"},"typeVersion":1},{"id":"ab87283e-ce39-4ed1-9659-ca9c2a613d5e","name":"Sticky Note8","type":"n8n-nodes-base.stickyNote","position":[-656,192],"parameters":{"color":5,"width":320,"height":272,"content":"For contacts with NO email history, extract domain from email address.\n\nIf we have valid domain - let's try to find contact pages."},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"f0b7ff51-f1b9-4294-b905-030605f45230","connections":{"3s":{"main":[[{"node":"GetInboxMessages","type":"main","index":0}]]},"Merge":{"main":[[{"node":"WriteToDB","type":"main","index":0}]]},"GetHTML":{"main":[[{"node":"Aggregate","type":"main","index":0}]]},"Aggregate":{"main":[[{"node":"Loop Over Items1","type":"main","index":0}]]},"WriteToDB":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"HTMLParser":{"main":[[{"node":"WriteToDB","type":"main","index":0}]]},"MailParser":{"main":[[{"node":"ValidDomain?","type":"main","index":0}]]},"SelectTo&CC":{"main":[[{"node":"Remove Duplicates","type":"main","index":0}]]},"GetClusters1":{"main":[[{"node":"HaveClusters","type":"main","index":0}]]},"HaveClusters":{"main":[[{"node":"Loop Over Items1","type":"main","index":0}],[{"node":"ReturnContactFromMailParser","type":"main","index":0}]]},"ValidDomain?":{"main":[[{"node":"SearchWebsite","type":"main","index":0}],[{"node":"No Operation, do nothing1","type":"main","index":0}]]},"SearchWebsite":{"main":[[{"node":"GetClusters1","type":"main","index":0}]]},"InboxMessages?":{"main":[[{"node":"SelectImportantFields","type":"main","index":0}],[{"node":"MailParser","type":"main","index":0}]]},"Loop Over Items":{"main":[[{"node":"No Operation, do nothing","type":"main","index":0}],[{"node":"3s","type":"main","index":0}]]},"GetInboxMessages":{"main":[[{"node":"InboxMessages?","type":"main","index":0}]]},"LoadSentMessages":{"main":[[{"node":"SelectTo&CC","type":"main","index":0}]]},"Loop Over Items1":{"main":[[{"node":"HTMLParser","type":"main","index":0}],[{"node":"GetHTML","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"Information Extractor","type":"ai_languageModel","index":0}]]},"Remove Duplicates":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"Information Extractor":{"main":[[{"node":"Merge","type":"main","index":0}]]},"SelectImportantFields":{"main":[[{"node":"Information Extractor","type":"main","index":0}]]},"No Operation, do nothing1":{"main":[[{"node":"WriteToDB","type":"main","index":0}]]},"ReturnContactFromMailParser":{"main":[[{"node":"WriteToDB","type":"main","index":0}]]},"When clicking ‘Execute workflow’":{"main":[[{"node":"LoadSentMessages","type":"main","index":0}]]}}} \ No newline at end of file