{
  "name": "LinkedIn Post Generator",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "linkedin-post",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "lip-0001-0001-0001-0001-000000000001",
      "name": "Post Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [240, 300],
      "webhookId": "linkedin-post-hook"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      },
      "id": "lip-sched-001-sched-001-sched-001-sched-001",
      "name": "Daily 9 AM IST",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [240, 100]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://raw.githubusercontent.com/saurabh7879/Automation/n8n/LinkedinPost/topics.csv",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "User-Agent", "value": "n8n-workflow" },
            { "name": "Accept", "value": "text/plain, */*" }
          ]
        },
        "options": {}
      },
      "id": "lip-0002-0002-0002-0002-000000000002",
      "name": "Fetch CSV",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [460, 300]
    },
    {
      "parameters": {
        "functionCode": "var j = items[0].json;\nvar raw = '';\nif (typeof j === 'string') { raw = j; }\nelse if (j && typeof j.data === 'string') { raw = j.data; }\nelse if (j && typeof j.body === 'string') { raw = j.body; }\nelse if (j) { for (var k in j) { if (typeof j[k] === 'string' && j[k].indexOf(',') >= 0) { raw = j[k]; break; } } }\n\nvar lines = raw.split(/\\r?\\n/).filter(function(l) { return l.trim(); });\nif (lines.length < 1) { return [{ json: { Topic: '' } }]; }\n\nfunction parseCSVLine(line) {\n  var result = [];\n  var current = '';\n  var inQuotes = false;\n  for (var i = 0; i < line.length; i++) {\n    var ch = line[i];\n    if (ch === '\"') { inQuotes = !inQuotes; }\n    else if (ch === ',' && !inQuotes) { result.push(current.trim()); current = ''; }\n    else { current += ch; }\n  }\n  result.push(current.trim());\n  return result;\n}\n\nvar rows = [];\nfor (var i = 0; i < lines.length; i++) {\n  var vals = parseCSVLine(lines[i]);\n  if (vals[0] && vals[0].trim()) {\n    rows.push({ Topic: vals[0] || '', Tone: vals[1] || 'professional', Audience: vals[2] || 'professionals' });\n  }\n}\n\nif (rows.length === 0) { return [{ json: { Topic: '' } }]; }\n\nvar IST_OFFSET_MS = 5.5 * 60 * 60 * 1000;\nvar dayIndex = Math.floor((Date.now() + IST_OFFSET_MS) / 86400000);\nvar picked = rows[dayIndex % rows.length];\npicked._dayIndex = dayIndex;\npicked._totalTopics = rows.length;\n\nreturn [{ json: picked }];"
      },
      "id": "lip-0003-0003-0003-0003-000000000003",
      "name": "Parse CSV",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [680, 300]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.Topic }}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "id": "lip-0004-0004-0004-0004-000000000004",
      "name": "Topic Exists?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [900, 300]
    },
    {
      "parameters": {
        "functionCode": "const topic = $input.first().json.Topic || '';\nconst tone = $input.first().json.Tone || 'professional';\n\nconst prompt = `You are a LinkedIn content expert. Write a LinkedIn post strictly following this format.\\n\\nTopic: ${topic}\\nTone: ${tone}\\n\\n---FORMAT TO FOLLOW EXACTLY---\\n\\nLine 1: [relevant emoji] [Topic Title]: [Catchy subtitle]\\n\\n[blank line]\\n\\nOne sentence explanation of what the topic is.\\n\\n[blank line]\\n\\n\\uD83D\\uDC49 Real-time Example: [Relatable real-world scenario]\\n[emoji] [sub-point 1]\\n[emoji] [sub-point 2]\\n[emoji] [sub-point 3]\\n[emoji] [sub-point 4]\\nAll are part of one closing sentence.\\n\\n[blank line]\\n\\n\\u26A1 Pros:\\n[positive point] [emoji]\\n[positive point] [emoji]\\n\\n[blank line]\\n\\n\\u26A0\\uFE0F Cons:\\n[negative point]\\n[negative point] [emoji]\\n\\n[blank line]\\n\\n\\uD83D\\uDCA1 In short: [One sentence summary with core tradeoff]\\n\\n[blank line]\\n\\n\\uD83D\\uDCAC Question for you: [Engaging question to spark comments]\\n\\n\\uD83D\\uDC47 Share your thoughts!\\n\\n#Hashtag1 #Hashtag2 #Hashtag3 #Hashtag4 #Hashtag5\\n---END FORMAT---\\n\\nReturn ONLY the post text.`;\n\nreturn [{ json: { prompt: prompt } }];"
      },
      "id": "lip-0004b-0004b-0004b-0004b-000000000004b",
      "name": "Build Prompt",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [1020, 200]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "Authorization", "value": "Bearer YOUR_OPENAI_API_KEY" },
            { "name": "Content-Type", "value": "application/json" }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { model: \"gpt-4o-mini\", messages: [ { role: \"user\", content: $json.prompt } ] } }}",
        "options": {}
      },
      "id": "lip-0005-0005-0005-0005-000000000005",
      "name": "Generate Post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1200, 200]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.linkedin.com/v2/ugcPosts",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "Authorization",             "value": "=Bearer AQV1cJUY3s12lg0SAMFt3J9kkVEM20VLYPg4KQ-tE3BLTo4OpjsrbTgPh79rxkkC9l5NasPmH_J2_u2zNEBOQfByrPiEjW8bEYFnmSKiRLmuAeP3SGEQ-gIyuGFXmQZ0ONnNTZAFf9v4GWJWVMhavsBuYOGQab0QZARP1U6VpEoSEGjGzGcrV8xnHK35WU6sRBXxFI9gzn5gZNe92o5HYM9iesSeK3D2uu9jQvC1ZgqaZsWnXLAlSt1Gbn7HTPg7-UkH42urztFS4Nl7qonwKG__sFo3Huva3m_GZg_2-lICmHBce_hO1r3B0UVw2kpDplwn2uMlliwuoMzBX_J7iw_w0uwnbw" },
            { "name": "Content-Type",              "value": "application/json" },
            { "name": "X-Restli-Protocol-Version", "value": "2.0.0" },
            { "name": "LinkedIn-Version",          "value": "202402" }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { author: \"urn:li:person:1H9p-wtG1P\", lifecycleState: \"PUBLISHED\", specificContent: { \"com.linkedin.ugc.ShareContent\": { shareCommentary: { text: $json.choices[0].message.content.trim() }, shareMediaCategory: \"NONE\" } }, visibility: { \"com.linkedin.ugc.MemberNetworkVisibility\": \"PUBLIC\" } } }}",
        "options": {}
      },
      "id": "lip-0006-0006-0006-0006-000000000006",
      "name": "Publish to LinkedIn",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1400, 200]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ ({  \"status\": \"success\",  \"topic\": $('Parse CSV').item.json.Topic,  \"linkedInPostId\": $json.id,  \"postContent\": $('Generate Post').item.json.choices[0].message.content.trim()}) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              { "name": "Access-Control-Allow-Origin", "value": "*" },
              { "name": "Content-Type",                "value": "application/json" }
            ]
          }
        }
      },
      "id": "lip-0007-0007-0007-0007-000000000007",
      "name": "Success Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1600, 200]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"status\": \"done\",\n  \"message\": \"No topics found in CSV. Add rows to linkedin-topics.csv and re-upload.\"\n}",
        "options": {
          "responseHeaders": {
            "entries": [
              { "name": "Access-Control-Allow-Origin", "value": "*" },
              { "name": "Content-Type",                "value": "application/json" }
            ]
          }
        }
      },
      "id": "lip-0008-0008-0008-0008-000000000008",
      "name": "No Topics Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1120, 440]
    }
  ],
  "connections": {
    "Post Webhook": {
      "main": [[{ "node": "Fetch CSV", "type": "main", "index": 0 }]]
    },
    "Daily 9 AM IST": {
      "main": [[{ "node": "Fetch CSV", "type": "main", "index": 0 }]]
    },
    "Fetch CSV": {
      "main": [[{ "node": "Parse CSV", "type": "main", "index": 0 }]]
    },
    "Parse CSV": {
      "main": [[{ "node": "Topic Exists?", "type": "main", "index": 0 }]]
    },
    "Topic Exists?": {
      "main": [
        [{ "node": "Build Prompt",       "type": "main", "index": 0 }],
        [{ "node": "No Topics Response", "type": "main", "index": 0 }]
      ]
    },
    "Build Prompt": {
      "main": [[{ "node": "Generate Post", "type": "main", "index": 0 }]]
    },
    "Generate Post": {
      "main": [[{ "node": "Publish to LinkedIn", "type": "main", "index": 0 }]]
    },
    "Publish to LinkedIn": {
      "main": [[{ "node": "Success Response", "type": "main", "index": 0 }]]
    }
  },
  "settings":   { "executionOrder": "v1", "timezone": "Asia/Kolkata" },
  "staticData": null,
  "tags":       [],
  "pinData":    {}
}
