{
  "id": "JfREzNspSXFdOSj6",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Smart Facebook Messenger Chatbot with OpenAI",
  "tags": [],
  "nodes": [
    {
      "id": "29e94220-042e-4373-8228-e74dc54ff92d",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        1136
      ],
      "parameters": {
        "color": 4,
        "width": 520,
        "height": 716,
        "content": "# Facebook Messenger AI Chatbot\n\n## Who is this for?\nBusinesses and developers who want to automate customer interactions on Facebook Messenger using AI.\n\n## What does it do?\n- Receives messages from Facebook Messenger\n- Batches rapid consecutive messages together\n- Processes messages with OpenAI (GPT-4o-mini)\n- Maintains conversation memory\n- Sends intelligent AI responses\n\n## Setup Requirements\n1. **Facebook App** with Messenger product enabled\n2. **Facebook Page** connected to the app\n3. **OpenAI API Key**\n\n## Quick Start\n1. Configure credentials (see setup notes)\n2. Set your verify token in \"Is Token Valid?\" node\n3. Publish the workflow\n4. Configure Facebook webhook URL\n5. Test by messaging your Facebook Page!\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2b182287-39cc-410c-8bff-3494a984c2d0",
      "name": "Sticky Note - Webhook Verification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        848
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 280,
        "content": "## 1. Webhook Verification\n\nFacebook sends a GET request to verify your webhook URL during setup.\n\n**How it works:**\n1. Facebook sends `hub.verify_token` and `hub.challenge`\n2. We validate the token matches your secret\n3. If valid, return the challenge to confirm\n4. If invalid, return 403 Forbidden\n\n\u26a0\ufe0f **Important:** Change `YOUR_VERIFY_TOKEN_HERE` in the \"Is Token Valid?\" node to your own secret token."
      },
      "typeVersion": 1
    },
    {
      "id": "36465ecc-f088-41c7-9074-eb8253b47935",
      "name": "Sticky Note - Message Receipt",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        1808
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 332,
        "content": "## 2. Receive & Filter Messages\n\nHandles incoming POST requests from Facebook when users send messages.\n\n**How it works:**\n1. Immediately acknowledge with `EVENT_RECEIVED` (required by Facebook)\n2. Filter out:\n   - Echo messages (messages we sent)\n   - Non-text messages\n   - Empty messages\n\n\u2705 Only valid user text messages proceed to processing."
      },
      "typeVersion": 1
    },
    {
      "id": "6dfe879f-861b-4d65-8f0f-54106887e80c",
      "name": "Sticky Note - Message Batching",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1408,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 740,
        "height": 300,
        "content": "## 3. Message Batching (Smart Feature!)\n\nUsers often send multiple quick messages. This section combines them into one request.\n\n**How it works:**\n1. Store each message in workflow static data\n2. Send \"seen\" indicator so user knows we received it\n3. Wait 3 seconds for additional messages\n4. Combine all messages from the same user\n5. Process as a single conversation turn\n\n\ud83d\udca1 **Benefit:** Reduces API calls and provides more context to the AI."
      },
      "typeVersion": 1
    },
    {
      "id": "f55d1c57-25f8-41ef-a306-5c620ffa0b3b",
      "name": "Sticky Note - AI Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2512,
        896
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 420,
        "content": "## 4. AI Agent Processing\n\nThe brain of the chatbot - powered by OpenAI.\n\n**Components:**\n- **OpenAI Chat Model:** Uses GPT-4o-mini for responses\n- **Conversation Memory:** Remembers last 50 messages per user\n- **AI Agent:** Processes messages with custom system prompt\n\n**Features:**\n- Maintains context across conversations\n- Friendly, concise responses\n- Respects Messenger's 2000 char limit\n\n\u270f\ufe0f **Customize:** Edit the system prompt in the AI Agent node to change personality/behavior."
      },
      "typeVersion": 1
    },
    {
      "id": "efed4d75-47f9-4617-8516-3c00d20d1ed3",
      "name": "Sticky Note - OpenAI Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2528,
        1792
      ],
      "parameters": {
        "color": 3,
        "width": 380,
        "height": 216,
        "content": "### OpenAI Credential Setup\n\n1. Go to [OpenAI API Keys](https://platform.openai.com/api-keys)\n2. Click **Create new secret key**\n3. Copy the key\n4. In n8n: Click the OpenAI node \u2192 Credential \u2192 Create New\n5. Paste your API key and save"
      },
      "typeVersion": 1
    },
    {
      "id": "b98c0b02-abe1-47a4-a014-bb04ce1136bf",
      "name": "Sticky Note - Response",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3136,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 280,
        "content": "## 5. Format & Send Response\n\n**How it works:**\n1. Format AI response for Messenger (remove markdown)\n2. Truncate if over 1900 characters\n3. Send via Facebook Graph API\n\n**Response includes:**\n- Proper recipient ID\n- Message type: RESPONSE\n- Clean, readable text"
      },
      "typeVersion": 1
    },
    {
      "id": "3740469b-f931-4df6-a194-71fa86d719c5",
      "name": "Sticky Note - Facebook Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        1728
      ],
      "parameters": {
        "color": 3,
        "width": 460,
        "height": 240,
        "content": "### Facebook Graph API Credential Setup\n\n1. Create a Facebook App at [developers.facebook.com](https://developers.facebook.com)\n2. Add **Messenger** product to your app\n3. Connect a Facebook Page\n4. Generate a **Page Access Token**\n5. In n8n: Create Facebook Graph API credential\n6. Paste the Page Access Token"
      },
      "typeVersion": 1
    },
    {
      "id": "6ff622e0-3d50-4f67-8f0e-ec22953e4672",
      "name": "Facebook Verification Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        720,
        1248
      ],
      "parameters": {
        "path": "facebook-messenger-webhook",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "c841bc40-d84d-4b35-9a75-4b9b6f009b02",
      "name": "Facebook Message Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        736,
        1600
      ],
      "parameters": {
        "path": "facebook-messenger-webhook",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "5d96d1ec-6682-47d4-a63f-36e217c56ef6",
      "name": "Is Token Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        944,
        1248
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "verify-token",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query['hub.verify_token'] }}",
              "rightValue": "YOUR_VERIFY_TOKEN_HERE"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ac029ab4-bec0-4b51-8973-35aede59cca8",
      "name": "Respond with Challenge",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1168,
        1168
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "={{ $json.query['hub.challenge'] }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "4e78dedb-7a5f-4f45-8ea5-3065f8b03ed3",
      "name": "Respond Forbidden",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1168,
        1328
      ],
      "parameters": {
        "options": {
          "responseCode": 403
        },
        "respondWith": "text",
        "responseBody": "Verification failed"
      },
      "typeVersion": 1.1
    },
    {
      "id": "1aa62451-1710-4506-920d-c13f11e22645",
      "name": "Acknowledge Event",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        960,
        1600
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "text",
        "responseBody": "EVENT_RECEIVED"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b45f4012-3783-45ac-a2ee-f52d7520dee0",
      "name": "Filter Valid Messages",
      "type": "n8n-nodes-base.if",
      "position": [
        1184,
        1600
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-message",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $json.body?.entry?.[0]?.messaging?.[0]?.message?.text }}",
              "rightValue": ""
            },
            {
              "id": "not-echo",
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.body?.entry?.[0]?.messaging?.[0]?.message?.is_echo }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c56ac1fa-3112-487c-8fa1-821dee72b0b8",
      "name": "Store Message for Batching",
      "type": "n8n-nodes-base.code",
      "position": [
        1408,
        1504
      ],
      "parameters": {
        "jsCode": "// Extract message data from Facebook webhook\nconst entry = $input.first().json.body?.entry?.[0];\nconst messaging = entry?.messaging?.[0];\n\nconst userId = messaging?.sender?.id;\nconst pageId = messaging?.recipient?.id;\nconst messageText = messaging?.message?.text;\nconst timestamp = messaging?.timestamp || Date.now();\nconst messageId = messaging?.message?.mid;\n\n// Store in workflow static data for batching\nconst staticData = $getWorkflowStaticData('global');\n\nif (!staticData.messageBatches) {\n  staticData.messageBatches = {};\n}\n\nif (!staticData.messageBatches[userId]) {\n  staticData.messageBatches[userId] = {\n    messages: [],\n    firstMessageTime: timestamp,\n    pageId: pageId\n  };\n}\n\nstaticData.messageBatches[userId].messages.push({\n  text: messageText,\n  timestamp: timestamp,\n  messageId: messageId\n});\n\nreturn {\n  json: {\n    userId,\n    pageId,\n    messageText,\n    timestamp,\n    messageId,\n    batchCount: staticData.messageBatches[userId].messages.length\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "00de23b7-473b-4bc4-9ac5-9f0dc8620f9c",
      "name": "Send Seen Indicator",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        1632,
        1504
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v21.0/me/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"recipient\": {\n    \"id\": \"{{ $json.userId }}\"\n  },\n  \"sender_action\": \"mark_seen\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi"
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "4dd996d2-59d0-4368-9f17-5898d562df27",
      "name": "Wait 3 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        1856,
        1504
      ],
      "parameters": {
        "amount": 3
      },
      "typeVersion": 1.1
    },
    {
      "id": "2daebc14-e6ee-433c-b6db-5be217686131",
      "name": "Retrieve Batched Messages",
      "type": "n8n-nodes-base.code",
      "position": [
        2080,
        1504
      ],
      "parameters": {
        "jsCode": "// Retrieve all batched messages for this user\n// Get userId from Store node directly (not from input, which is HTTP response)\nconst userId = $('Store Message for Batching').first().json.userId;\nconst pageId = $('Store Message for Batching').first().json.pageId;\n\nconst staticData = $getWorkflowStaticData('global');\nconst userBatch = staticData.messageBatches?.[userId];\n\nif (!userBatch || userBatch.messages.length === 0) {\n  return { json: { skip: true } };\n}\n\n// Combine all messages into one\nconst combinedMessage = userBatch.messages\n  .sort((a, b) => a.timestamp - b.timestamp)\n  .map(m => m.text)\n  .join(' ');\n\n// Clear the batch\ndelete staticData.messageBatches[userId];\n\nreturn {\n  json: {\n    userId,\n    pageId,\n    combinedMessage,\n    messageCount: userBatch.messages.length,\n    skip: false\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "18eb9501-1b62-442d-9b1d-c8b501361488",
      "name": "Has Messages to Process?",
      "type": "n8n-nodes-base.if",
      "position": [
        2304,
        1504
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "not-skip",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.skip }}",
              "rightValue": false
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ed3ce29f-2fdb-4241-89c6-d3374a1c4e22",
      "name": "Send Typing Indicator",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        2528,
        1408
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v21.0/me/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"recipient\": {\n    \"id\": \"{{ $json.userId }}\"\n  },\n  \"sender_action\": \"typing_on\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi"
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "473c7fd9-6cd6-4e22-ae62-776849b6d459",
      "name": "Conversation Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        2928,
        1616
      ],
      "parameters": {
        "sessionKey": "={{ $('Retrieve Batched Messages').first().json.userId }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 50
      },
      "typeVersion": 1.3
    },
    {
      "id": "6b373a33-256e-45ea-90ad-da63037541ed",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2672,
        1632
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "1c266c66-727b-454f-87aa-3a3a03a76d17",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2752,
        1408
      ],
      "parameters": {
        "text": "={{ $('Retrieve Batched Messages').first().json.combinedMessage }}",
        "options": {
          "systemMessage": "You are a helpful and friendly AI assistant for Facebook Messenger. Your responses should be:\n\n1. Concise and conversational - suitable for chat format\n2. Friendly but professional\n3. Helpful and informative\n4. Under 2000 characters (Messenger limit)\n\nIf the user asks about services or products, provide helpful information. Always be polite and end conversations gracefully when appropriate.\n\nRemember: You're chatting via Messenger, so keep responses appropriate for that medium."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "b05f4415-1a09-453c-a596-af35640d23c2",
      "name": "Format Response",
      "type": "n8n-nodes-base.code",
      "position": [
        3152,
        1408
      ],
      "parameters": {
        "jsCode": "// Format AI response for Facebook Messenger\nconst aiResponse = $input.first().json.output || $input.first().json.text || '';\nconst userId = $('Retrieve Batched Messages').first().json.userId;\nconst pageId = $('Retrieve Batched Messages').first().json.pageId;\n\n// Messenger has a 2000 character limit per message\nlet formattedResponse = aiResponse.trim();\n\n// If response is too long, truncate it\nif (formattedResponse.length > 1900) {\n  formattedResponse = formattedResponse.substring(0, 1897) + '...';\n}\n\n// Remove any markdown formatting that doesn't render well in Messenger\nformattedResponse = formattedResponse\n  .replace(/\\*\\*(.+?)\\*\\*/g, '$1')  // Remove bold\n  .replace(/\\*(.+?)\\*/g, '$1')      // Remove italic\n  .replace(/```[\\s\\S]*?```/g, (match) => match.replace(/```\\w*\\n?/g, ''))  // Remove code blocks\n  .replace(/`(.+?)`/g, '$1');       // Remove inline code\n\nreturn {\n  json: {\n    userId,\n    pageId,\n    response: formattedResponse\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "727d6fa4-f63e-4fa4-89d6-52ceee36bd0f",
      "name": "Send Response to User",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        3376,
        1408
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v21.0/me/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"recipient\": {\n    \"id\": \"{{ $json.userId }}\"\n  },\n  \"messaging_type\": \"RESPONSE\",\n  \"message\": {\n    \"text\": {{ JSON.stringify($json.response) }}\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi"
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "ea0091d9-c884-42a7-935a-bca4b29ad16b",
      "name": "Success",
      "type": "n8n-nodes-base.set",
      "position": [
        3600,
        1408
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "42b3f7cb-02c5-4e92-b01b-72b3fb2e389b",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Seconds": {
      "main": [
        [
          {
            "node": "Retrieve Batched Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response": {
      "main": [
        [
          {
            "node": "Send Response to User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Token Valid?": {
      "main": [
        [
          {
            "node": "Respond with Challenge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Forbidden",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Acknowledge Event": {
      "main": [
        [
          {
            "node": "Filter Valid Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Send Seen Indicator": {
      "main": [
        [
          {
            "node": "Wait 3 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid Messages": {
      "main": [
        [
          {
            "node": "Store Message for Batching",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Response to User": {
      "main": [
        [
          {
            "node": "Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Typing Indicator": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Facebook Message Webhook": {
      "main": [
        [
          {
            "node": "Acknowledge Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Messages to Process?": {
      "main": [
        [
          {
            "node": "Send Typing Indicator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Batched Messages": {
      "main": [
        [
          {
            "node": "Has Messages to Process?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Message for Batching": {
      "main": [
        [
          {
            "node": "Send Seen Indicator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Facebook Verification Webhook": {
      "main": [
        [
          {
            "node": "Is Token Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}