AutomationFlowsSocial Media › Reply to Instagram and Facebook Comments via Meta Webhooks and Dms

Reply to Instagram and Facebook Comments via Meta Webhooks and Dms

ByPawel @pavvel on n8n.io

This workflow receives Meta webhooks for Instagram and Facebook Page comments, verifies the webhook subscription handshake, and then posts an optional public reply and/or sends a private DM to the commenter via the Meta Graph API. Receives an incoming webhook request from Meta…

Webhook trigger★★★★☆ complexity13 nodesHTTP Request
Social Media Trigger: Webhook Nodes: 13 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16300 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Auto-reply to Instagram and Facebook comments with a public reply and DM",
  "nodes": [
    {
      "id": "webhook",
      "name": "Webhook (Meta)",
      "type": "n8n-nodes-base.webhook",
      "onError": "continueRegularOutput",
      "position": [
        0,
        400
      ],
      "parameters": {
        "path": "meta-auto-reply",
        "options": {},
        "responseMode": "responseNode",
        "multipleMethods": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "config",
      "name": "Configuration (EDIT ME)",
      "type": "n8n-nodes-base.set",
      "position": [
        300,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "access_token",
              "type": "string",
              "value": "PASTE_YOUR_ACCESS_TOKEN_HERE"
            },
            {
              "id": "c2",
              "name": "verify_token",
              "type": "string",
              "value": "my-secret-token-123"
            },
            {
              "id": "c3",
              "name": "keyword",
              "type": "string",
              "value": ""
            },
            {
              "id": "c4",
              "name": "comment_reply",
              "type": "string",
              "value": "Thanks for the comment! \ud83d\ude4c Check your DMs \ud83d\udce9"
            },
            {
              "id": "c5",
              "name": "dm_message",
              "type": "string",
              "value": "Hey! Thanks for commenting. Here's the link I promised: https://your-website.com \ud83d\ude80"
            },
            {
              "id": "c6",
              "name": "api_version",
              "type": "string",
              "value": "23.0"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "verify",
      "name": "Meta Verification?",
      "type": "n8n-nodes-base.if",
      "position": [
        600,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "is-subscribe",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query ? ($json.query['hub.mode'] || '') : '' }}",
              "rightValue": "subscribe"
            },
            {
              "id": "token-matches",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query ? ($json.query['hub.verify_token'] || '') : '' }}",
              "rightValue": "={{ $json.verify_token }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "respond-challenge",
      "name": "Respond hub.challenge",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        900,
        240
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "={{ $json.query['hub.challenge'] }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "respond-200",
      "name": "Respond 200 OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        900,
        400
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "noData"
      },
      "typeVersion": 1.4
    },
    {
      "id": "extract",
      "name": "Extract Comment",
      "type": "n8n-nodes-base.code",
      "position": [
        1200,
        400
      ],
      "parameters": {
        "jsCode": "// Extracts IG/FB comments from the Meta webhook payload and builds\n// ready-to-use Graph API requests (public reply + private DM).\n// Skips: own comments (echo / auto-replies) and comments that don't\n// match the keyword filter (if set).\nconst item = $input.first().json;\nconst body = item.body || {};\nif (!body.entry || (body.object !== 'instagram' && body.object !== 'page')) return [];\n\nconst normalize = (t) => (t || '').toLowerCase().trim().replace(/\\s+/g, '');\nconst keyword = normalize(item.keyword);\nconst out = [];\n\nfor (const entry of body.entry) {\n  const accountId = String(entry.id);\n  for (const change of entry.changes || []) {\n    const v = change.value || {};\n    let commentId, text, fromId, fromName, platform;\n\n    if (body.object === 'instagram' && change.field === 'comments') {\n      platform = 'instagram';\n      commentId = v.id;\n      text = v.text || '';\n      fromId = v.from && v.from.id;\n      fromName = (v.from && v.from.username) || '';\n    } else if (body.object === 'page' && change.field === 'feed' && v.item === 'comment' && v.verb === 'add') {\n      platform = 'facebook';\n      commentId = v.comment_id;\n      text = v.message || '';\n      fromId = v.from && v.from.id;\n      fromName = (v.from && v.from.name) || '';\n    } else {\n      continue; // other events (DMs, likes, stories etc.) are ignored\n    }\n\n    if (!commentId || !fromId) continue;\n    if (String(fromId) === accountId) continue; // own comment -> loop protection\n    if (keyword && !normalize(text).includes(keyword)) continue;\n\n    const host = platform === 'instagram' ? 'graph.instagram.com' : 'graph.facebook.com';\n    const base = `https://${host}/v${item.api_version}`;\n\n    const replyText = item.comment_reply || '';\n    const dmText = item.dm_message || '';\n\n    out.push({\n      json: {\n        platform,\n        account_id: accountId,\n        comment_id: commentId,\n        comment_text: text,\n        from_id: String(fromId),\n        from_name: fromName,\n        // ready-to-use request: public reply under the comment\n        reply_url: replyText\n          ? (platform === 'instagram' ? `${base}/${commentId}/replies` : `${base}/${commentId}/comments`)\n          : '',\n        reply_payload: { message: replyText },\n        // ready-to-use request: private message (private reply)\n        dm_url: dmText ? `${base}/${accountId}/messages` : '',\n        dm_payload: { recipient: { comment_id: commentId }, message: { text: dmText } },\n      },\n    });\n  }\n}\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "if-comment",
      "name": "Send Public Reply?",
      "type": "n8n-nodes-base.if",
      "position": [
        1500,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-if-comment",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.reply_url }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "if-dm",
      "name": "Send DM?",
      "type": "n8n-nodes-base.if",
      "position": [
        1500,
        560
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-if-dm",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.dm_url }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "http-comment",
      "name": "Reply to Comment",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        1800,
        240
      ],
      "parameters": {
        "url": "={{ $json.reply_url }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json.reply_payload }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Configuration (EDIT ME)').first().json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "http-dm",
      "name": "Send DM (private reply)",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        1800,
        560
      ],
      "parameters": {
        "url": "={{ $json.dm_url }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json.dm_payload }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Configuration (EDIT ME)').first().json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "Sticky Note",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        -500
      ],
      "parameters": {
        "color": 1,
        "width": 600,
        "height": 600,
        "content": "## Auto-Reply to Instagram & Facebook Comments\n\nAutomatically reply under new Instagram or Facebook comments and send the commenter a private DM \u2014 no database, no queue, no third-party tool like ManyChat.\n\n### How it works\n- A **webhook** receives comment events from the Meta Graph API and answers Meta's verification handshake.\n- The workflow responds `200 OK` immediately, then parses the event, skips your own comments (loop protection) and any comment that does not match your optional keyword.\n- It posts a public reply under the comment and sends a private message, each via the Meta Graph API.\n\n### Setup\n1. Open **Configuration (EDIT ME)** and fill in your `access_token`, a `verify_token` of your choice, an optional `keyword`, and your reply + DM text.\n2. Activate the workflow and copy the **Production URL** from the *Webhook (Meta)* node.\n3. In your Meta app, add it as a webhook with the same `verify_token` and subscribe to `comments` (Instagram) and/or `feed` (Facebook Page). The app must be in **Live** mode.\n\n### Customization tips\nLeave the reply or DM text empty to skip that step. Set a keyword to trigger only on matching comments.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "Sticky Note1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 1140,
        "height": 420,
        "content": "## 1. Receive & verify the webhook\nMeta sends comment events here. The IF answers Meta's one-time verification (hub.challenge); every real event gets an immediate 200 OK. Add your tokens and reply text in **Configuration (EDIT ME)**.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "Sticky Note2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1140,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 860,
        "height": 580,
        "content": "## 2. Reply to the comment & send a DM\nParses the comment, skips your own (loop protection) and non-matching keywords, then posts a public reply and a private DM through the Meta Graph API.\n"
      },
      "typeVersion": 1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Send DM?": {
      "main": [
        [
          {
            "node": "Send DM (private reply)",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Respond 200 OK": {
      "main": [
        [
          {
            "node": "Extract Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook (Meta)": {
      "main": [
        [
          {
            "node": "Configuration (EDIT ME)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Configuration (EDIT ME)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Comment": {
      "main": [
        [
          {
            "node": "Send Public Reply?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send DM?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Meta Verification?": {
      "main": [
        [
          {
            "node": "Respond hub.challenge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond 200 OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Public Reply?": {
      "main": [
        [
          {
            "node": "Reply to Comment",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Configuration (EDIT ME)": {
      "main": [
        [
          {
            "node": "Meta Verification?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow receives Meta webhooks for Instagram and Facebook Page comments, verifies the webhook subscription handshake, and then posts an optional public reply and/or sends a private DM to the commenter via the Meta Graph API. Receives an incoming webhook request from Meta…

Source: https://n8n.io/workflows/16300/ — original creator credit. Request a take-down →

More Social Media workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Social Media

A powerful workflow to scrape Reddit posts and comments by keywords and/or subreddit, with intelligent filtering and formatting. Search Reddit - Accepts keywords and/or subreddit parameters via webhoo

HTTP Request Tool, Reddit, HTTP Request
Social Media

This n8n workflow is an automated Instagram DM responder that checks messages and replies with affiliate links based on keywords.

HTTP Request, Read Write File
Social Media

🚀 Discover trending and viral YouTube videos easily with this powerful n8n automation! This workflow helps you perform bulk research on YouTube videos related to any search term, analyzing engagement

HTTP Request, Google Sheets, Form Trigger
Social Media

Works on both n8n Cloud and self-hosted instances. This template uses the community node, which is installable on n8n Cloud and self-hosted setups.

Form Trigger, N8N Nodes Renderio, HTTP Request +1
Social Media

This n8n workflow automatically monitors positive reactions on Facebook Page posts, stores them in Airtable and notifies your team in Slack to create a motivational “Wall of Love.” It runs on a schedu

HTTP Request, Airtable, Slack