{
  "nodes": [
    {
      "id": "8a56f296-d0fb-4bb2-ac01-9a7ce94f70c3",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        944
      ],
      "parameters": {
        "width": 480,
        "height": 760,
        "content": "## \ud83d\udcec Instagram DM Auto-Responder\n\n### How it works\n\n1. Triggers every 15 minutes (or manually via Start Monitoring).\n2. Fetches all conversations from 4 DM folders simultaneously.\n3. Deduplicates conversations and iterates each one.\n4. Loads replied history and product keyword list from JSON files.\n5. Sends a personalised affiliate link reply for any new keyword match.\n6. Records replied message IDs to prevent duplicate sends.\n\n### Setup steps\n\n- [ ] Replace `[YOUR-META-ACCESS-TOKEN]` in **Configure Global Settings** with your long-lived Meta Page access token.\n- [ ] Replace `[YOUR-INSTAGRAM-ACCOUNT-ID]` with your Instagram Business/Creator account numeric ID.\n- [ ] Create `/home/node/.n8n-files/products.json` with your keyword \u2192 affiliate URL mappings.\n- [ ] Create `/home/node/.n8n-files/replied_dms.json` with `{ \"replied_message_ids\": [] }` (auto-created on first successful reply).\n- [ ] Ensure your Meta app has `instagram_manage_messages` and `pages_messaging` permissions.\n\n### Customisation\n\nEdit the reply template inside **Filter New DMs** (look for `replyMessage`). Change the polling interval in **Schedule Trigger**."
      },
      "typeVersion": 1
    },
    {
      "id": "c1ed17c9-9b1b-4e31-9fe4-f79e38336128",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1200,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 464,
        "content": "## Trigger & Configuration\n\nRuns every 15 minutes (or manually). Sets the Meta access token and Instagram account ID used by all downstream API calls."
      },
      "typeVersion": 1
    },
    {
      "id": "925d6555-210e-4a78-ba2c-dcf09815b535",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        928
      ],
      "parameters": {
        "color": 5,
        "width": 656,
        "height": 848,
        "content": "## Fetch All DM Folders\n\nPolls all 4 Instagram DM folders in parallel (Inbox, General, Pending, Message Requests). Results are merged and deduplicated by conversation ID."
      },
      "typeVersion": 1
    },
    {
      "id": "5a1515d6-65d8-484b-9f2d-b4702ce3f70f",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2336,
        1152
      ],
      "parameters": {
        "color": 5,
        "width": 1536,
        "height": 336,
        "content": "## Filter & Match Keywords\n\nIterates each conversation one-by-one. Fetches messages, loads replied-ID history and products.json, then checks if any new message contains a product keyword. Skips messages already replied to or sent by the account owner."
      },
      "typeVersion": 1
    },
    {
      "id": "90ead610-aaa3-43d8-9c37-94ad212061e9",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3904,
        1040
      ],
      "parameters": {
        "color": 5,
        "width": 1088,
        "height": 544,
        "content": "## Reply & Record\n\nSends the affiliate link reply via the Meta Graph API. Immediately appends the message ID to replied_dms.json to prevent duplicate replies."
      },
      "typeVersion": 1
    },
    {
      "id": "c5496bb7-dadd-4154-90cf-98b7912bb9c3",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1248,
        1424
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "659bf768-dbce-4404-b70a-556a8628c364",
      "name": "Start Monitoring",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        1248,
        1232
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "02b6c8b7-eccd-4695-9658-7737360448cd",
      "name": "Configure Global Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        1472,
        1328
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "accessToken",
              "name": "accessToken",
              "type": "string",
              "value": "[YOUR-META-ACCESS-TOKEN]"
            },
            {
              "id": "igUserId",
              "name": "igUserId",
              "type": "string",
              "value": "[YOUR-INSTAGRAM-ACCOUNT-ID]"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "e774d814-a32d-4219-a81c-afb563c57218",
      "name": "Get Inbox Conversations",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1696,
        1040
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $('Configure Global Settings').first().json.igUserId }}/conversations",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "platform",
              "value": "instagram"
            },
            {
              "name": "folder",
              "value": "inbox"
            },
            {
              "name": "fields",
              "value": "id,participants"
            },
            {
              "name": "access_token",
              "value": "={{ $('Configure Global Settings').first().json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "916db3b8-6b4a-4e36-9dfc-6746a76e1c1a",
      "name": "Get General Conversations",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1696,
        1232
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $('Configure Global Settings').first().json.igUserId }}/conversations",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "platform",
              "value": "instagram"
            },
            {
              "name": "folder",
              "value": "general"
            },
            {
              "name": "fields",
              "value": "id,participants"
            },
            {
              "name": "access_token",
              "value": "={{ $('Configure Global Settings').first().json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "991b6655-3b16-4359-8fbd-c74e51535203",
      "name": "Get Pending Conversations",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1696,
        1424
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $('Configure Global Settings').first().json.igUserId }}/conversations",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "platform",
              "value": "instagram"
            },
            {
              "name": "folder",
              "value": "pending"
            },
            {
              "name": "fields",
              "value": "id,participants"
            },
            {
              "name": "access_token",
              "value": "={{ $('Configure Global Settings').first().json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "48c02ce3-dfae-4584-b131-a9846c90d120",
      "name": "Get Message Requests",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1696,
        1616
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $('Configure Global Settings').first().json.igUserId }}/conversations",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "platform",
              "value": "instagram"
            },
            {
              "name": "folder",
              "value": "message_requests"
            },
            {
              "name": "fields",
              "value": "id,participants"
            },
            {
              "name": "access_token",
              "value": "={{ $('Configure Global Settings').first().json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "d01ea650-0c1d-41c1-99ee-399c9075500b",
      "name": "Merge All Conversations1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1936,
        1136
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "multiplex"
      },
      "typeVersion": 2.1
    },
    {
      "id": "2bd82f88-864b-4a22-9114-7ed76cf95eaf",
      "name": "Merge All Conversations2",
      "type": "n8n-nodes-base.merge",
      "position": [
        1936,
        1520
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "multiplex"
      },
      "typeVersion": 2.1
    },
    {
      "id": "46b64f3c-b2bc-4e5d-adf7-c9a6e1d3bf44",
      "name": "Merge All Conversations",
      "type": "n8n-nodes-base.merge",
      "position": [
        2160,
        1328
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "multiplex"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ae6d9272-aea1-4e84-93f7-880aa52b76c1",
      "name": "Parse Conversations",
      "type": "n8n-nodes-base.code",
      "position": [
        2384,
        1328
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nlet allConversations = [];\nfor (const item of items) {\n  const data = item.json.data || [];\n  allConversations = allConversations.concat(data);\n}\n// Deduplicate by conversation id\nconst seen = new Set();\nconst unique = allConversations.filter(c => {\n  if (seen.has(c.id)) return false;\n  seen.add(c.id);\n  return true;\n});\nreturn unique.map(c => ({ json: c }));"
      },
      "typeVersion": 2
    },
    {
      "id": "d20e1312-b723-40ec-a3f0-1eddaad9f558",
      "name": "Split Conversations",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        2608,
        1328
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 1
    },
    {
      "id": "5cb303e0-c6b4-48c2-8dd0-2d42b9d31fb7",
      "name": "Get Messages",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        2832,
        1248
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $json.id }}/messages",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "fields",
              "value": "id,message,from,created_time"
            },
            {
              "name": "access_token",
              "value": "={{ $('Configure Global Settings').first().json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "273a5945-86a7-4455-b661-44862274f27f",
      "name": "Read DM History",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        3056,
        1248
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/home/node/.n8n-files/replied_dms.json"
      },
      "typeVersion": 1
    },
    {
      "id": "0c8ee32c-b38b-4e91-b780-f3e30be09ac5",
      "name": "Read products.json",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        3280,
        1248
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/home/node/.n8n-files/products.json"
      },
      "typeVersion": 1
    },
    {
      "id": "cad1e107-8430-4dd4-bfcb-81b828e2a6db",
      "name": "Filter New DMs",
      "type": "n8n-nodes-base.code",
      "position": [
        3504,
        1248
      ],
      "parameters": {
        "jsCode": "try {\n  const messages = $('Get Messages').item.json.data || [];\n  const conversationId = $('Split Conversations').item.json.id;\n  const participants = $('Split Conversations').item.json.participants?.data || [];\n  const igUserId = $('Configure Global Settings').first().json.igUserId;\n  const accessToken = $('Configure Global Settings').first().json.accessToken;\n\n  // Get replied history\n  const historyNode = $('Read DM History').item;\n  let history = [];\n  if (historyNode.binary) {\n    const binaryKey = Object.keys(historyNode.binary)[0];\n    const buffer = await this.helpers.getBinaryDataBuffer(0, binaryKey);\n    history = JSON.parse(buffer.toString('utf8')).replied_message_ids || [];\n  } else {\n    history = historyNode.json.replied_message_ids || [];\n  }\n\n  // Get products\n  const productsNode = $('Read products.json').item;\n  let products = [];\n  if (productsNode.binary) {\n    const binaryKey = Object.keys(productsNode.binary)[0];\n    const buffer = await this.helpers.getBinaryDataBuffer(0, binaryKey);\n    products = JSON.parse(buffer.toString('utf8')).products || [];\n  } else {\n    products = productsNode.json.products || [];\n  }\n\n  // Find the sender (not our IG account)\n  const sender = participants.find(p => p.id !== igUserId);\n  if (!sender) return [{ json: { decision: 'skip' } }];\n\n  // Filter messages: not replied, from sender (not us), contains a keyword\n  const toProcess = [];\n  for (const msg of messages) {\n    if (history.includes(msg.id)) continue;\n    if (msg.from?.id === igUserId) continue;\n    const text = (msg.message || '').toLowerCase();\n\n    for (const product of products) {\n      const keyword = (product.keyword || 'link').toLowerCase();\n      if (text.includes(keyword)) {\n        toProcess.push({\n          json: {\n            decision: 'process',\n            messageId: msg.id,\n            senderId: sender.id,\n            senderName: sender.name || 'there',\n            keyword: product.keyword,\n            productTitle: product.title,\n            affiliateUrl: product.affiliateUrl,\n            replyMessage: `Hey! You asked for the link to ${product.title}. Here it is: ${product.affiliateUrl} \ud83c\udf89`,\n            accessToken,\n            igUserId\n          }\n        });\n        break;\n      }\n    }\n  }\n\n  if (toProcess.length === 0) {\n    return [{ json: { decision: 'skip' } }];\n  }\n\n  return toProcess;\n} catch(e) {\n  return [{ json: { decision: 'skip', error: e.message } }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "536cff78-ad6d-406a-9aee-0044d30b4c54",
      "name": "Check If Should Reply",
      "type": "n8n-nodes-base.if",
      "position": [
        3728,
        1248
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "conditions": [
            {
              "id": "is_process",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.decision }}",
              "rightValue": "process"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3cfdd5e9-bcd1-4b69-8deb-6f548374fd8d",
      "name": "Split DMs",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3952,
        1200
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 1
    },
    {
      "id": "bcaa226d-63ef-4a38-badf-839c67f0c235",
      "name": "NoOp - Skip",
      "type": "n8n-nodes-base.noOp",
      "position": [
        3952,
        1424
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b4ae8ff1-0d51-4b29-971f-159d256b9792",
      "name": "Send DM Reply",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4176,
        1136
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{ $('Configure Global Settings').first().json.igUserId }}/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"recipient\": { \"id\": \"{{ $json.senderId }}\" },\n  \"message\": { \"text\": \"{{ $json.replyMessage }}\" },\n  \"access_token\": \"{{ $json.accessToken }}\"\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.1
    },
    {
      "id": "87453c5e-5a43-4a86-a8d8-d7b9645a67a4",
      "name": "Read Fresh DM History",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        4400,
        1136
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/home/node/.n8n-files/replied_dms.json"
      },
      "typeVersion": 1
    },
    {
      "id": "b64105a7-7629-4119-9d03-41df9924fb41",
      "name": "Prepare DM History Update",
      "type": "n8n-nodes-base.code",
      "position": [
        4624,
        1136
      ],
      "parameters": {
        "jsCode": "const messageId = $('Split DMs').item.json.messageId;\nconst historyNode = $('Read Fresh DM History').item;\nlet historyObj = { replied_message_ids: [] };\n\nif (historyNode.binary) {\n  const binaryKey = Object.keys(historyNode.binary)[0];\n  const buffer = await this.helpers.getBinaryDataBuffer(0, binaryKey);\n  historyObj = JSON.parse(buffer.toString('utf8'));\n} else {\n  historyObj = historyNode.json;\n}\n\nif (!historyObj.replied_message_ids.includes(messageId)) {\n  historyObj.replied_message_ids.push(messageId);\n}\n\nreturn historyObj;"
      },
      "typeVersion": 2
    },
    {
      "id": "ffcd5bf1-6b03-478a-a515-b9cd85e042ca",
      "name": "Save DM History",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        4848,
        1200
      ],
      "parameters": {
        "options": {},
        "fileName": "/home/node/.n8n-files/replied_dms.json",
        "operation": "write"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Split DMs": {
      "main": [
        [
          {
            "node": "Send DM Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NoOp - Skip": {
      "main": [
        [
          {
            "node": "Split Conversations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Messages": {
      "main": [
        [
          {
            "node": "Read DM History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send DM Reply": {
      "main": [
        [
          {
            "node": "Read Fresh DM History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter New DMs": {
      "main": [
        [
          {
            "node": "Check If Should Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read DM History": {
      "main": [
        [
          {
            "node": "Read products.json",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save DM History": {
      "main": [
        [
          {
            "node": "Split DMs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Configure Global Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Monitoring": {
      "main": [
        [
          {
            "node": "Configure Global Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read products.json": {
      "main": [
        [
          {
            "node": "Filter New DMs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Conversations": {
      "main": [
        [
          {
            "node": "Split Conversations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Conversations": {
      "main": [
        [
          {
            "node": "Get Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Message Requests": {
      "main": [
        [
          {
            "node": "Merge All Conversations2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Should Reply": {
      "main": [
        [
          {
            "node": "Split DMs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "NoOp - Skip",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Fresh DM History": {
      "main": [
        [
          {
            "node": "Prepare DM History Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Inbox Conversations": {
      "main": [
        [
          {
            "node": "Merge All Conversations1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Conversations": {
      "main": [
        [
          {
            "node": "Parse Conversations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Conversations1": {
      "main": [
        [
          {
            "node": "Merge All Conversations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Conversations2": {
      "main": [
        [
          {
            "node": "Merge All Conversations",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Configure Global Settings": {
      "main": [
        [
          {
            "node": "Get Inbox Conversations",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get General Conversations",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Pending Conversations",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Message Requests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get General Conversations": {
      "main": [
        [
          {
            "node": "Merge All Conversations1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Pending Conversations": {
      "main": [
        [
          {
            "node": "Merge All Conversations2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare DM History Update": {
      "main": [
        [
          {
            "node": "Save DM History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}