{ "updatedAt": "2026-05-14T00:01:22.299Z", "createdAt": "2026-05-12T17:48:01.214Z", "id": "GSmzuA5dgGgyRg5v", "name": "Web-to-Notes Capture (Local LLM + Obsidian)", "description": null, "active": true, "isArchived": false, "nodes": [ { "parameters": { "httpMethod": "POST", "path": "web-to-notes", "responseMode": "responseNode", "options": {} }, "id": "02979a5e-67e7-43ae-8c9f-4694a5b36e56", "name": "Webhook - Capture URL", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -900, 0 ], "webhookId": "7958ecbc-c714-41d5-a829-882447ab95f8" }, { "parameters": { "jsCode": "const body = $json.body ?? $json;\nconst url = String(body.url || body.link || '').trim();\nif (!url || !/^https?:\\/\\//i.test(url)) throw new Error('POST JSON must include url starting with http:// or https://');\nconst title = String(body.title || '').trim();\nconst notes = String(body.notes || body.note || body.comment || '').trim();\nconst tags = Array.isArray(body.tags) ? body.tags : String(body.tags || 'web-capture').split(',').map(s => s.trim()).filter(Boolean);\nreturn [{ json: { url, title, notes, tags, capturedAt: new Date().toISOString() } }];" }, "id": "22ba0ac9-af51-4469-a8bd-b3d3c1dd049b", "name": "Normalize Input", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -680, 0 ] }, { "parameters": { "method": "POST", "url": "http://172.19.0.1:18806/v1/chat/completions", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ model: \"gemma-4-26b\", messages: [{ role: \"system\", content: \"You are a concise summarizer. Extract key points, claims, and notable details. Format as clear markdown with a summary section and key points list.\" }, { role: \"user\", content: `Summarize this ${$json.content_type || \"web\"} content titled \"${$json.title || \"untitled\"}\":\\n\\n${($json.text || \"\").slice(0, 8000)}` }], temperature: 0.3, max_tokens: 1600 }) }}", "options": { "timeout": 120000 } }, "id": "2ea254be-4a88-426a-97ff-16a80196b462", "name": "Summarize with llama.cpp", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 0, 0 ], "continueOnFail": true }, { "parameters": { "jsCode": "const extracted = $('Extract Content').first().json;\nconst input = $('Normalize Input').first().json;\n\nlet summary = '';\ntry { summary = $json.choices?.[0]?.message?.content || $json.body?.choices?.[0]?.message?.content || ''; } catch (e) {}\n// Dedent summary (LLM sometimes returns indented markdown)\nsummary = summary.split('\\n').map(l => l.replace(/^\\s{4}/, '')).join('\\n').trim();\nif (!summary) summary = 'LLM summary unavailable.\\n\\nContent excerpt:\\n\\n> ' + (extracted.text || '').slice(0, 1200);\n\nconst contentType = extracted.content_type || 'web';\nconst title = extracted.title || input.title || 'Untitled';\nconst sourceUrl = extracted.metadata?.source_url || input.url;\nconst notes = input.notes || '';\nconst tags = input.tags || ['web-capture'];\n\nif (contentType === 'youtube') tags.push('youtube', 'video-transcript');\nelse if (contentType === 'pdf') tags.push('pdf', 'document');\n\nconst meta = extracted.metadata || {};\nlet metaSection = '';\nif (contentType === 'youtube') {\n metaSection = `**Video ID:** ${meta.video_id || 'N/A'} \\n**Transcript Entries:** ${meta.transcript_entries || 0}`;\n} else if (contentType === 'pdf') {\n metaSection = `**Author:** ${meta.author || 'N/A'} \\n**Pages:** ${meta.page_count || 'N/A'}`;\n}\n\nfunction slugify(s) { return String(s || 'untitled').toLowerCase().replace(/https?:\\/\\//,'').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,80) || 'untitled'; }\nfunction yamlSafe(s) { return String(s || '').replace(/'/g, \"''\").replace(/\\n/g, ' '); }\n\nconst date = new Date().toISOString().split('T')[0];\nconst notePath = `Clippings/${date}-${slugify(title)}.md`;\n\nconst frontmatter = [\n '---',\n `title: '${yamlSafe(title)}'`,\n `source_url: ${sourceUrl}`,\n `content_type: ${contentType}`,\n `date: ${date}`,\n `tags: [${tags.map(t => \"'\" + t + \"'\").join(', ')}]`,\n '---',\n].join('\\n');\n\nconst body = [\n frontmatter,\n '',\n `# ${title}`,\n '',\n `> Source: [${title}](${sourceUrl})`,\n ...(metaSection ? ['', metaSection] : []),\n ...(notes ? ['', `## Notes\\n${notes}`] : []),\n '',\n '## Summary',\n '',\n summary,\n '',\n '---',\n `*Captured via Web-to-Notes (${contentType})*`,\n].join('\\n');\n\nreturn [{ json: { notePath, body, title, contentType, sourceUrl } }];\n" }, "id": "403dff8b-5789-4018-89ec-69d45569cd25", "name": "Build Markdown Note", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 220, 0 ] }, { "parameters": { "method": "PUT", "url": "={{'http://172.19.0.1:27123/vault/' + encodeURIComponent($json.notePath).replace(/%2F/g, '/')}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "text/markdown" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "text/markdown", "body": "={{$json.body}}", "options": { "timeout": 30000 }, "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth" }, "id": "1d00b920-985e-415c-b445-4a28674287a0", "name": "Write Note to Obsidian", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 460, 0 ], "credentials": { "httpHeaderAuth": { "id": "465Swz2b71O2KRAK", "name": "Obsidian Local REST API" } } }, { "parameters": { "respondWith": "json", "responseBody": "={{JSON.stringify({ok: true, notePath: $json.notePath, title: $json.title, source: $json.url})}}", "options": {} }, "id": "c3d45b9e-a4d3-43ee-855a-7a76030e8888", "name": "Respond", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.5, "position": [ 700, 0 ] }, { "parameters": { "method": "POST", "url": "http://172.19.0.1:18812/extract", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ url: $json.url }) }}", "options": { "timeout": 120000, "fullResponse": false } }, "id": "extract-content-v2", "name": "Extract Content", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ -240, 0 ] } ], "connections": { "Webhook - Capture URL": { "main": [ [ { "node": "Normalize Input", "type": "main", "index": 0 } ] ] }, "Normalize Input": { "main": [ [ { "node": "Extract Content", "type": "main", "index": 0 } ] ] }, "Extract Content": { "main": [ [ { "node": "Summarize with llama.cpp", "type": "main", "index": 0 } ] ] }, "Summarize with llama.cpp": { "main": [ [ { "node": "Build Markdown Note", "type": "main", "index": 0 } ] ] }, "Build Markdown Note": { "main": [ [ { "node": "Write Note to Obsidian", "type": "main", "index": 0 } ] ] }, "Write Note to Obsidian": { "main": [ [ { "node": "Respond", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "callerPolicy": "workflowsFromSameOwner", "availableInMCP": false }, "staticData": null, "meta": null, "pinData": null, "versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08", "activeVersionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08", "versionCounter": 30, "triggerCount": 1, "shared": [ { "updatedAt": "2026-05-12T17:48:01.217Z", "createdAt": "2026-05-12T17:48:01.217Z", "role": "workflow:owner", "workflowId": "GSmzuA5dgGgyRg5v", "projectId": "WGdp8QunI1tHpjXa", "project": { "updatedAt": "2026-03-11T21:08:10.005Z", "createdAt": "2026-03-11T21:05:11.541Z", "id": "WGdp8QunI1tHpjXa", "name": "will will ", "type": "personal", "icon": null, "description": null, "creatorId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5" } } ], "tags": [], "activeVersion": { "updatedAt": "2026-05-14T00:01:22.300Z", "createdAt": "2026-05-14T00:01:22.300Z", "versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08", "workflowId": "GSmzuA5dgGgyRg5v", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "web-to-notes", "responseMode": "responseNode", "options": {} }, "id": "02979a5e-67e7-43ae-8c9f-4694a5b36e56", "name": "Webhook - Capture URL", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -900, 0 ], "webhookId": "7958ecbc-c714-41d5-a829-882447ab95f8" }, { "parameters": { "jsCode": "const body = $json.body ?? $json;\nconst url = String(body.url || body.link || '').trim();\nif (!url || !/^https?:\\/\\//i.test(url)) throw new Error('POST JSON must include url starting with http:// or https://');\nconst title = String(body.title || '').trim();\nconst notes = String(body.notes || body.note || body.comment || '').trim();\nconst tags = Array.isArray(body.tags) ? body.tags : String(body.tags || 'web-capture').split(',').map(s => s.trim()).filter(Boolean);\nreturn [{ json: { url, title, notes, tags, capturedAt: new Date().toISOString() } }];" }, "id": "22ba0ac9-af51-4469-a8bd-b3d3c1dd049b", "name": "Normalize Input", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -680, 0 ] }, { "parameters": { "method": "POST", "url": "http://172.19.0.1:18806/v1/chat/completions", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ model: \"gemma-4-26b\", messages: [{ role: \"system\", content: \"You are a concise summarizer. Extract key points, claims, and notable details. Format as clear markdown with a summary section and key points list.\" }, { role: \"user\", content: `Summarize this ${$json.content_type || \"web\"} content titled \"${$json.title || \"untitled\"}\":\\n\\n${($json.text || \"\").slice(0, 8000)}` }], temperature: 0.3, max_tokens: 1600 }) }}", "options": { "timeout": 120000 } }, "id": "2ea254be-4a88-426a-97ff-16a80196b462", "name": "Summarize with llama.cpp", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 0, 0 ], "continueOnFail": true }, { "parameters": { "jsCode": "const extracted = $('Extract Content').first().json;\nconst input = $('Normalize Input').first().json;\n\nlet summary = '';\ntry { summary = $json.choices?.[0]?.message?.content || $json.body?.choices?.[0]?.message?.content || ''; } catch (e) {}\n// Dedent summary (LLM sometimes returns indented markdown)\nsummary = summary.split('\\n').map(l => l.replace(/^\\s{4}/, '')).join('\\n').trim();\nif (!summary) summary = 'LLM summary unavailable.\\n\\nContent excerpt:\\n\\n> ' + (extracted.text || '').slice(0, 1200);\n\nconst contentType = extracted.content_type || 'web';\nconst title = extracted.title || input.title || 'Untitled';\nconst sourceUrl = extracted.metadata?.source_url || input.url;\nconst notes = input.notes || '';\nconst tags = input.tags || ['web-capture'];\n\nif (contentType === 'youtube') tags.push('youtube', 'video-transcript');\nelse if (contentType === 'pdf') tags.push('pdf', 'document');\n\nconst meta = extracted.metadata || {};\nlet metaSection = '';\nif (contentType === 'youtube') {\n metaSection = `**Video ID:** ${meta.video_id || 'N/A'} \\n**Transcript Entries:** ${meta.transcript_entries || 0}`;\n} else if (contentType === 'pdf') {\n metaSection = `**Author:** ${meta.author || 'N/A'} \\n**Pages:** ${meta.page_count || 'N/A'}`;\n}\n\nfunction slugify(s) { return String(s || 'untitled').toLowerCase().replace(/https?:\\/\\//,'').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,80) || 'untitled'; }\nfunction yamlSafe(s) { return String(s || '').replace(/'/g, \"''\").replace(/\\n/g, ' '); }\n\nconst date = new Date().toISOString().split('T')[0];\nconst notePath = `Clippings/${date}-${slugify(title)}.md`;\n\nconst frontmatter = [\n '---',\n `title: '${yamlSafe(title)}'`,\n `source_url: ${sourceUrl}`,\n `content_type: ${contentType}`,\n `date: ${date}`,\n `tags: [${tags.map(t => \"'\" + t + \"'\").join(', ')}]`,\n '---',\n].join('\\n');\n\nconst body = [\n frontmatter,\n '',\n `# ${title}`,\n '',\n `> Source: [${title}](${sourceUrl})`,\n ...(metaSection ? ['', metaSection] : []),\n ...(notes ? ['', `## Notes\\n${notes}`] : []),\n '',\n '## Summary',\n '',\n summary,\n '',\n '---',\n `*Captured via Web-to-Notes (${contentType})*`,\n].join('\\n');\n\nreturn [{ json: { notePath, body, title, contentType, sourceUrl } }];\n" }, "id": "403dff8b-5789-4018-89ec-69d45569cd25", "name": "Build Markdown Note", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 220, 0 ] }, { "parameters": { "method": "PUT", "url": "={{'http://172.19.0.1:27123/vault/' + encodeURIComponent($json.notePath).replace(/%2F/g, '/')}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "text/markdown" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "text/markdown", "body": "={{$json.body}}", "options": { "timeout": 30000 }, "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth" }, "id": "1d00b920-985e-415c-b445-4a28674287a0", "name": "Write Note to Obsidian", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 460, 0 ], "credentials": { "httpHeaderAuth": { "id": "465Swz2b71O2KRAK", "name": "Obsidian Local REST API" } } }, { "parameters": { "respondWith": "json", "responseBody": "={{JSON.stringify({ok: true, notePath: $json.notePath, title: $json.title, source: $json.url})}}", "options": {} }, "id": "c3d45b9e-a4d3-43ee-855a-7a76030e8888", "name": "Respond", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.5, "position": [ 700, 0 ] }, { "parameters": { "method": "POST", "url": "http://172.19.0.1:18812/extract", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ url: $json.url }) }}", "options": { "timeout": 120000, "fullResponse": false } }, "id": "extract-content-v2", "name": "Extract Content", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ -240, 0 ] } ], "connections": { "Webhook - Capture URL": { "main": [ [ { "node": "Normalize Input", "type": "main", "index": 0 } ] ] }, "Normalize Input": { "main": [ [ { "node": "Extract Content", "type": "main", "index": 0 } ] ] }, "Extract Content": { "main": [ [ { "node": "Summarize with llama.cpp", "type": "main", "index": 0 } ] ] }, "Summarize with llama.cpp": { "main": [ [ { "node": "Build Markdown Note", "type": "main", "index": 0 } ] ] }, "Build Markdown Note": { "main": [ [ { "node": "Write Note to Obsidian", "type": "main", "index": 0 } ] ] }, "Write Note to Obsidian": { "main": [ [ { "node": "Respond", "type": "main", "index": 0 } ] ] } }, "authors": "will will", "name": null, "description": null, "autosaved": false, "workflowPublishHistory": [ { "createdAt": "2026-05-14T00:01:22.328Z", "id": 1462, "workflowId": "GSmzuA5dgGgyRg5v", "versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08", "event": "activated", "userId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5" }, { "createdAt": "2026-05-14T00:01:22.316Z", "id": 1461, "workflowId": "GSmzuA5dgGgyRg5v", "versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08", "event": "deactivated", "userId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5" } ] } }