{
  "id": "Gu90aB2mHSZG9Eku",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "NapkinAI - Convert Raw Ideas into Visual Diagrams & Content Assets",
  "tags": [],
  "nodes": [
    {
      "id": "23c25b65-5d99-4ebd-9948-771109ec0ce4",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        352
      ],
      "parameters": {
        "width": 760,
        "height": 1100,
        "content": "# \ud83c\udfa8 NapkinAI Visual Idea Processor\n\nAutomates the transformation of raw text ideas into structured visual diagrams and content assets using NapkinAI.\n\n## \ud83d\udd04 Workflow Stages\n\n### 1. **Input Collection**\n   - Webhook trigger accepts raw idea text\n   - Manual trigger for testing\n   - Configurable idea metadata (title, type, audience)\n\n### 2. **AI Idea Enrichment (Claude)**\n   - Expands raw idea into structured concept\n   - Extracts key themes and relationships\n   - Suggests optimal diagram types\n   - Generates multiple visual concepts\n\n### 3. **NapkinAI Processing**\n   - Authenticate with NapkinAI API\n   - Submit enriched text for visual generation\n   - Poll for completion status\n   - Retrieve generated diagram assets\n\n### 4. **Asset Management**\n   - Download generated visuals\n   - Store metadata in database\n   - Organize by project/category\n\n### 5. **Delivery**\n   - Email with visual assets attached\n   - Slack notification with preview\n   - Save to Google Drive / Notion\n\n## \u2699\ufe0f Setup Required\n- NapkinAI API credentials\n- Anthropic API key (Claude)\n- SMTP for email delivery\n- Slack webhook (optional)\n- PostgreSQL for logging (optional)"
      },
      "typeVersion": 1
    },
    {
      "id": "949207a4-d9ae-4c69-bf43-0dac877f44ca",
      "name": "Sticky Note - Input",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        432
      ],
      "parameters": {
        "color": 6,
        "width": 644,
        "height": 832,
        "content": "## \ud83d\udce5 INPUT TRIGGERS\nWebhook or manual trigger to start idea processing pipeline"
      },
      "typeVersion": 1
    },
    {
      "id": "1b07ce2e-9cca-405d-a78d-d036ba43bb99",
      "name": "Sticky Note - Enrichment",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        432
      ],
      "parameters": {
        "color": 3,
        "width": 680,
        "height": 816,
        "content": "## \ud83e\udd16 AI ENRICHMENT (CLAUDE)\nExpand idea, extract structure, suggest diagram types, generate prompts for NapkinAI"
      },
      "typeVersion": 1
    },
    {
      "id": "ddeb75dd-ea3e-4bca-b235-6e935a049824",
      "name": "Sticky Note - NapkinAI",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2192,
        432
      ],
      "parameters": {
        "color": 4,
        "width": 800,
        "height": 816,
        "content": "## \ud83d\uddbc\ufe0f NAPKINAI VISUAL GENERATION\nAuthenticate \u2192 Submit text \u2192 Poll status \u2192 Retrieve visual assets"
      },
      "typeVersion": 1
    },
    {
      "id": "d85ea2bb-1656-4a12-aeb9-89f42988b013",
      "name": "Sticky Note - Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3024,
        432
      ],
      "parameters": {
        "color": 5,
        "width": 1196,
        "height": 816,
        "content": "## \ud83d\udce8 ASSET DELIVERY & LOGGING\nProcess assets, send notifications, log to database, deliver to team"
      },
      "typeVersion": 1
    },
    {
      "id": "cf63e6ba-91f7-45d5-b125-2ab24a2ee5ec",
      "name": "Webhook - Receive Idea",
      "type": "n8n-nodes-base.webhook",
      "notes": "Accepts POST requests with raw idea text and metadata",
      "position": [
        1088,
        1056
      ],
      "parameters": {
        "path": "napkin-idea",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "d20aa7b4-b4b7-4368-ab47-1d81dbe69d92",
      "name": "Manual Trigger - Test",
      "type": "n8n-nodes-base.manualTrigger",
      "notes": "Use this for testing the workflow manually",
      "position": [
        864,
        864
      ],
      "parameters": {},
      "notesInFlow": true,
      "typeVersion": 1
    },
    {
      "id": "d9105729-f9f4-4d80-89a1-fa10a94e6c3b",
      "name": "Set Test Idea Data",
      "type": "n8n-nodes-base.set",
      "notes": "Provides sample idea data when using manual trigger",
      "position": [
        1088,
        864
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "field-title",
              "name": "body.title",
              "type": "string",
              "value": "How SaaS Companies Scale from 0 to 1M Users"
            },
            {
              "id": "field-idea",
              "name": "body.rawIdea",
              "type": "string",
              "value": "SaaS companies grow through product-led growth, viral loops, and network effects. Early traction comes from solving a specific pain point, then expanding. Key phases: discovery, activation, retention, referral, revenue. Marketing channels evolve from founder-led sales to inbound content to paid acquisition. Infrastructure must scale horizontally. Team grows from generalists to specialists."
            },
            {
              "id": "field-type",
              "name": "body.diagramType",
              "type": "string",
              "value": "flowchart"
            },
            {
              "id": "field-audience",
              "name": "body.targetAudience",
              "type": "string",
              "value": "startup founders"
            },
            {
              "id": "field-style",
              "name": "body.visualStyle",
              "type": "string",
              "value": "professional"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "6e98c0a0-c3e7-416a-baed-e0b5b57b9410",
      "name": "Merge Input Sources",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines webhook and manual trigger inputs into single flow",
      "position": [
        1312,
        960
      ],
      "parameters": {
        "mode": "chooseBranch"
      },
      "notesInFlow": true,
      "typeVersion": 3
    },
    {
      "id": "c089a60b-3499-412b-ad9b-9602f7a0fa38",
      "name": "Validate & Normalize Input",
      "type": "n8n-nodes-base.code",
      "notes": "Validates required fields and normalizes input structure",
      "position": [
        1536,
        960
      ],
      "parameters": {
        "jsCode": "// Validate and normalize input\nconst input = $input.first().json;\nconst body = input.body || input;\n\n// Required field check\nif (!body.rawIdea || body.rawIdea.trim().length < 10) {\n  throw new Error('rawIdea is required and must be at least 10 characters');\n}\n\n// Normalize and set defaults\nconst normalized = {\n  ideaId: `IDEA-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`,\n  title: body.title || 'Untitled Idea',\n  rawIdea: body.rawIdea.trim(),\n  diagramType: body.diagramType || 'auto',  // auto, flowchart, mindmap, timeline, comparison, process\n  targetAudience: body.targetAudience || 'general',\n  visualStyle: body.visualStyle || 'professional',  // professional, casual, minimal, bold\n  outputFormats: body.outputFormats || ['png', 'svg'],\n  projectTag: body.projectTag || 'general',\n  priority: body.priority || 'normal',\n  requestedAt: new Date().toISOString(),\n  wordCount: body.rawIdea.trim().split(/\\s+/).length\n};\n\nconsole.log(`\u2705 Validated idea: ${normalized.ideaId} - \"${normalized.title}\"`);\n\nreturn [{ json: normalized }];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "2e37c716-8657-4dd7-ba39-091fab3dff5d",
      "name": "AI - Enrich & Structure Idea",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Claude analyzes the raw idea and generates structured content for NapkinAI",
      "position": [
        1760,
        960
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "claude-sonnet-4-20250514"
            },
            {
              "name": "max_tokens",
              "value": "2000"
            },
            {
              "name": "system",
              "value": "You are an expert at transforming raw ideas into structured visual concepts optimized for diagram generation. You understand visual hierarchy, information design, and how to represent complex concepts clearly. Always respond with valid JSON only \u2014 no preamble, no markdown fences."
            },
            {
              "name": "messages",
              "value": "={{ [{role: 'user', content: `Analyze this raw idea and produce structured content optimized for visual diagram generation with NapkinAI.\n\nTitle: ${$json.title}\nRaw Idea: ${$json.rawIdea}\nDiagram Type Preference: ${$json.diagramType}\nTarget Audience: ${$json.targetAudience}\nVisual Style: ${$json.visualStyle}\n\nReturn a JSON object with these exact keys:\n{\n  \"enrichedText\": \"A clear, concise version of the idea optimized for visual representation (150-250 words). Use active voice and concrete language. Structure it so key concepts and their relationships are explicit.\",\n  \"diagramType\": \"Best diagram type for this content: flowchart | mindmap | timeline | comparison | process | hierarchy | cycle\",\n  \"napkinPrompt\": \"A direct, specific prompt for NapkinAI to generate the ideal visual (2-3 sentences focusing on layout and key elements)\",\n  \"keyNodes\": [\"Array of 5-8 core concepts/nodes that must appear in the diagram\"],\n  \"relationships\": [\"Array of 4-6 key relationships between nodes, e.g., 'A leads to B', 'X requires Y'\"],\n  \"visualHierarchy\": \"Description of which elements should be most prominent\",\n  \"colorScheme\": \"Suggested color palette description for the visual style requested\",\n  \"alternativeFormats\": [\n    {\n      \"format\": \"Instagram Carousel\",\n      \"slideCount\": 5,\n      \"description\": \"How to adapt this as a carousel post\"\n    },\n    {\n      \"format\": \"LinkedIn Infographic\",\n      \"sections\": [\"section names\"],\n      \"description\": \"How to adapt as LinkedIn visual\"\n    },\n    {\n      \"format\": \"Twitter Thread Visual\",\n      \"tweetCount\": 6,\n      \"description\": \"How to break into tweet thread with visuals\"\n    }\n  ],\n  \"contentAngles\": [\n    {\n      \"angle\": \"Educational\",\n      \"hook\": \"Opening hook for this angle\",\n      \"caption\": \"Social caption for this angle\"\n    },\n    {\n      \"angle\": \"Thought Leadership\",\n      \"hook\": \"Opening hook for this angle\",\n      \"caption\": \"Social caption for this angle\"\n    },\n    {\n      \"angle\": \"Practical How-To\",\n      \"hook\": \"Opening hook for this angle\",\n      \"caption\": \"Social caption for this angle\"\n    }\n  ],\n  \"estimatedComplexity\": \"simple | moderate | complex\",\n  \"recommendedDimensions\": {\n    \"width\": 1200,\n    \"height\": 675,\n    \"ratio\": \"16:9\"\n  }\n}`}] }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $credentials.apiKey }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "anthropicApi"
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "f1bc0f02-b9d1-497c-823e-79b5d0ace99a",
      "name": "Parse Claude Enrichment",
      "type": "n8n-nodes-base.code",
      "notes": "Extracts and merges Claude's structured analysis with original idea data",
      "position": [
        2224,
        960
      ],
      "parameters": {
        "jsCode": "// Parse Claude AI enrichment response\nconst claudeResponse = $input.first().json;\nconst ideaData = $node['Validate & Normalize Input'].first().json;\n\ntry {\n  const rawText = claudeResponse.content?.[0]?.text || '';\n  \n  // Strip any accidental markdown fences\n  const clean = rawText.replace(/```json|```/g, '').trim();\n  const enrichment = JSON.parse(clean);\n  \n  return [{\n    json: {\n      // Original idea data\n      ...ideaData,\n      // AI enrichment\n      enrichedText: enrichment.enrichedText,\n      diagramType: enrichment.diagramType || ideaData.diagramType,\n      napkinPrompt: enrichment.napkinPrompt,\n      keyNodes: enrichment.keyNodes || [],\n      relationships: enrichment.relationships || [],\n      visualHierarchy: enrichment.visualHierarchy,\n      colorScheme: enrichment.colorScheme,\n      alternativeFormats: enrichment.alternativeFormats || [],\n      contentAngles: enrichment.contentAngles || [],\n      estimatedComplexity: enrichment.estimatedComplexity || 'moderate',\n      recommendedDimensions: enrichment.recommendedDimensions || { width: 1200, height: 675, ratio: '16:9' },\n      // Status tracking\n      enrichedAt: new Date().toISOString(),\n      stage: 'enriched'\n    }\n  }];\n\n} catch (error) {\n  console.error('Failed to parse Claude response:', error.message);\n  // Fallback: use raw idea as enriched text\n  return [{\n    json: {\n      ...ideaData,\n      enrichedText: ideaData.rawIdea,\n      napkinPrompt: `Create a ${ideaData.diagramType || 'flowchart'} diagram visualizing: ${ideaData.rawIdea.substring(0, 200)}`,\n      keyNodes: [],\n      relationships: [],\n      alternativeFormats: [],\n      contentAngles: [],\n      estimatedComplexity: 'moderate',\n      enrichedAt: new Date().toISOString(),\n      stage: 'enriched-fallback',\n      parseError: error.message\n    }\n  }];\n}"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "5a4ff79a-de79-452c-9188-b7e94c9e5c1c",
      "name": "NapkinAI - Authenticate",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Authenticates with NapkinAI and retrieves access token",
      "position": [
        2416,
        960
      ],
      "parameters": {
        "url": "https://api.napkin.ai/v1/auth/token",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "apiKey",
              "value": "={{ $vars.NAPKIN_API_KEY }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "5a733d85-d7c2-4c77-91f9-e9eeb4cc34f9",
      "name": "NapkinAI - Submit Idea for Visual",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Submits the enriched text to NapkinAI to generate a visual diagram",
      "position": [
        2640,
        960
      ],
      "parameters": {
        "url": "https://api.napkin.ai/v1/visuals/generate",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ $node['Parse Claude Enrichment'].json.enrichedText }}"
            },
            {
              "name": "prompt",
              "value": "={{ $node['Parse Claude Enrichment'].json.napkinPrompt }}"
            },
            {
              "name": "diagramType",
              "value": "={{ $node['Parse Claude Enrichment'].json.diagramType }}"
            },
            {
              "name": "style",
              "value": "={{ $node['Parse Claude Enrichment'].json.visualStyle }}"
            },
            {
              "name": "outputFormats",
              "value": "={{ $node['Parse Claude Enrichment'].json.outputFormats }}"
            },
            {
              "name": "dimensions",
              "value": "={{ $node['Parse Claude Enrichment'].json.recommendedDimensions }}"
            },
            {
              "name": "metadata",
              "value": "={{ { ideaId: $node['Parse Claude Enrichment'].json.ideaId, title: $node['Parse Claude Enrichment'].json.title } }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['NapkinAI - Authenticate'].json.accessToken }}"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "b058695b-9eae-4c06-a0ab-83f4d6ea83f7",
      "name": "NapkinAI - Poll Generation Status",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Polls NapkinAI job status until visual generation is complete",
      "position": [
        2864,
        960
      ],
      "parameters": {
        "url": "=https://api.napkin.ai/v1/visuals/{{ $node['NapkinAI - Submit Idea for Visual'].json.jobId }}/status",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['NapkinAI - Authenticate'].json.accessToken }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "33ad463b-ea93-43c7-9800-4f894ae4ab09",
      "name": "Check Job Completion",
      "type": "n8n-nodes-base.if",
      "notes": "Routes based on whether NapkinAI job is complete or still processing",
      "position": [
        3088,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "status-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.2
    },
    {
      "id": "1e21e958-c193-44cf-9207-5de63b19018f",
      "name": "Wait 5s - Retry Poll",
      "type": "n8n-nodes-base.wait",
      "notes": "Waits 5 seconds before polling again if job not complete",
      "position": [
        3312,
        1056
      ],
      "parameters": {},
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "ff5ad3ed-f1be-4a94-8be7-b376b53d3180",
      "name": "NapkinAI - Retrieve Generated Assets",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Downloads all generated visual assets from NapkinAI",
      "position": [
        3312,
        816
      ],
      "parameters": {
        "url": "=https://api.napkin.ai/v1/visuals/{{ $node['NapkinAI - Submit Idea for Visual'].json.jobId }}/assets",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['NapkinAI - Authenticate'].json.accessToken }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "53fee705-076d-4a61-8622-53809ba0ce62",
      "name": "Assemble Full Output Package",
      "type": "n8n-nodes-base.code",
      "notes": "Combines idea data, AI enrichment, and NapkinAI assets into final output",
      "position": [
        3536,
        816
      ],
      "parameters": {
        "jsCode": "// Assemble complete output package\nconst napkinAssets = $input.first().json;\nconst ideaData = $node['Parse Claude Enrichment'].first().json;\nconst jobData = $node['NapkinAI - Submit Idea for Visual'].first().json;\n\nconst outputPackage = {\n  // Identity\n  ideaId: ideaData.ideaId,\n  title: ideaData.title,\n  completedAt: new Date().toISOString(),\n  processingTime: `${((new Date() - new Date(ideaData.requestedAt)) / 1000).toFixed(1)}s`,\n  \n  // Original input\n  input: {\n    rawIdea: ideaData.rawIdea,\n    diagramTypeRequested: ideaData.diagramType,\n    targetAudience: ideaData.targetAudience,\n    visualStyle: ideaData.visualStyle\n  },\n  \n  // AI Analysis\n  analysis: {\n    enrichedText: ideaData.enrichedText,\n    diagramTypeSelected: ideaData.diagramType,\n    keyNodes: ideaData.keyNodes,\n    relationships: ideaData.relationships,\n    estimatedComplexity: ideaData.estimatedComplexity,\n    colorScheme: ideaData.colorScheme\n  },\n  \n  // Generated visuals\n  visuals: {\n    jobId: jobData.jobId,\n    mainDiagram: napkinAssets.assets?.find(a => a.type === 'primary') || napkinAssets.assets?.[0],\n    allAssets: napkinAssets.assets || [],\n    downloadUrls: (napkinAssets.assets || []).map(a => ({ format: a.format, url: a.downloadUrl, size: a.size })),\n    thumbnailUrl: napkinAssets.thumbnailUrl || ''\n  },\n  \n  // Content strategy\n  contentStrategy: {\n    alternativeFormats: ideaData.alternativeFormats,\n    contentAngles: ideaData.contentAngles,\n    napkinPrompt: ideaData.napkinPrompt\n  },\n  \n  // Quick summary for notifications\n  summary: {\n    assetCount: (napkinAssets.assets || []).length,\n    formats: (napkinAssets.assets || []).map(a => a.format),\n    topAngle: ideaData.contentAngles?.[0]?.hook || 'Visual ready',\n    bestFormat: ideaData.alternativeFormats?.[0]?.format || 'Diagram'\n  }\n};\n\nreturn [{ json: outputPackage }];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "cdaa0017-bd85-471b-ac56-4c84ecc79d45",
      "name": "Send Email - Visual Assets Delivery",
      "type": "n8n-nodes-base.emailSend",
      "notes": "Delivers generated visual assets and content strategy via email",
      "position": [
        3760,
        640
      ],
      "parameters": {
        "options": {},
        "subject": "=\ud83c\udfa8 Visual Ready: {{ $json.title }} ({{ $json.summary.assetCount }} assets generated)",
        "toEmail": "={{ $vars.DELIVERY_EMAIL || 'user@example.com' }}",
        "fromEmail": "user@example.com"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "c38f5e85-08c6-4d01-a0a3-95fb53e6fae6",
      "name": "Send Slack - Quick Notification",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Posts a quick Slack notification with visual preview and key links",
      "position": [
        3760,
        832
      ],
      "parameters": {
        "url": "={{ $vars.SLACK_WEBHOOK_URL }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\ud83c\udfa8 New visual generated!"
            },
            {
              "name": "blocks",
              "value": "={{ JSON.stringify([\n  {\n    type: 'header',\n    text: { type: 'plain_text', text: '\ud83c\udfa8 Visual Ready: ' + $json.title }\n  },\n  {\n    type: 'section',\n    fields: [\n      { type: 'mrkdwn', text: '*Diagram Type:* ' + $json.analysis.diagramTypeSelected },\n      { type: 'mrkdwn', text: '*Complexity:* ' + $json.analysis.estimatedComplexity },\n      { type: 'mrkdwn', text: '*Assets:* ' + $json.summary.assetCount + ' files' },\n      { type: 'mrkdwn', text: '*ID:* ' + $json.ideaId }\n    ]\n  },\n  {\n    type: 'section',\n    text: { type: 'mrkdwn', text: '*Top Hook:* _' + ($json.summary.topAngle || 'Visual ready') + '_' }\n  },\n  {\n    type: 'actions',\n    elements: $json.visuals.downloadUrls.slice(0,2).map(u => ({\n      type: 'button',\n      text: { type: 'plain_text', text: 'Download ' + u.format.toUpperCase() },\n      url: u.url\n    }))\n  }\n]) }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "dbf5b473-12a9-435b-a74f-5bbdbec99981",
      "name": "Log to Database",
      "type": "n8n-nodes-base.postgres",
      "notes": "Stores complete output record in PostgreSQL for analytics and retrieval",
      "position": [
        3760,
        1024
      ],
      "parameters": {
        "table": "napkin_visuals",
        "schema": "content",
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.4
    },
    {
      "id": "ac3e98e1-9562-4637-9eac-fca383fdae42",
      "name": "Mark Idea as Processed",
      "type": "n8n-nodes-base.code",
      "notes": "Tracks processed ideas in workflow static data to avoid reprocessing",
      "position": [
        3984,
        768
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Track completed visuals\nconst output = $input.item.json;\nconst staticData = $getWorkflowStaticData('global');\n\nif (!staticData.processedIdeas) {\n  staticData.processedIdeas = {};\n}\n\n// Clean entries older than 90 days\nconst ninetyDaysAgo = Date.now() - (90 * 24 * 60 * 60 * 1000);\nfor (const key in staticData.processedIdeas) {\n  if (staticData.processedIdeas[key].timestamp < ninetyDaysAgo) {\n    delete staticData.processedIdeas[key];\n  }\n}\n\n// Mark as processed\nstaticData.processedIdeas[output.ideaId] = {\n  ideaId: output.ideaId,\n  title: output.title,\n  jobId: output.visuals?.jobId,\n  assetCount: output.summary?.assetCount,\n  completedAt: output.completedAt,\n  timestamp: Date.now()\n};\n\nconsole.log(`\u2705 Marked complete: ${output.ideaId} - ${output.title}`);\n\nreturn {\n  json: {\n    status: 'success',\n    ideaId: output.ideaId,\n    title: output.title,\n    message: `Visual generation complete. ${output.summary?.assetCount || 0} assets delivered.`,\n    assets: output.visuals?.downloadUrls || [],\n    contentAngles: output.contentStrategy?.contentAngles || [],\n    alternativeFormats: output.contentStrategy?.alternativeFormats || []\n  }\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "898d005b-06e9-4509-8d40-7378283ef587",
      "name": "Wait For Data",
      "type": "n8n-nodes-base.wait",
      "position": [
        1968,
        960
      ],
      "parameters": {},
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "dbbdd357-3e81-43b9-9929-fc0f506053f6",
  "connections": {
    "Wait For Data": {
      "main": [
        [
          {
            "node": "Parse Claude Enrichment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Database": {
      "main": [
        [
          {
            "node": "Mark Idea as Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Test Idea Data": {
      "main": [
        [
          {
            "node": "Merge Input Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Input Sources": {
      "main": [
        [
          {
            "node": "Validate & Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Job Completion": {
      "main": [
        [
          {
            "node": "NapkinAI - Retrieve Generated Assets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 5s - Retry Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5s - Retry Poll": {
      "main": [
        [
          {
            "node": "NapkinAI - Poll Generation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger - Test": {
      "main": [
        [
          {
            "node": "Set Test Idea Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Idea": {
      "main": [
        [
          {
            "node": "Merge Input Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NapkinAI - Authenticate": {
      "main": [
        [
          {
            "node": "NapkinAI - Submit Idea for Visual",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Claude Enrichment": {
      "main": [
        [
          {
            "node": "NapkinAI - Authenticate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate & Normalize Input": {
      "main": [
        [
          {
            "node": "AI - Enrich & Structure Idea",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Enrich & Structure Idea": {
      "main": [
        [
          {
            "node": "Wait For Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assemble Full Output Package": {
      "main": [
        [
          {
            "node": "Send Email - Visual Assets Delivery",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack - Quick Notification",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Slack - Quick Notification": {
      "main": [
        [
          {
            "node": "Mark Idea as Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NapkinAI - Poll Generation Status": {
      "main": [
        [
          {
            "node": "Check Job Completion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NapkinAI - Submit Idea for Visual": {
      "main": [
        [
          {
            "node": "NapkinAI - Poll Generation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email - Visual Assets Delivery": {
      "main": [
        [
          {
            "node": "Mark Idea as Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NapkinAI - Retrieve Generated Assets": {
      "main": [
        [
          {
            "node": "Assemble Full Output Package",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}