{
  "id": "qUaYvDCRcnKqeaeD",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Facebook Group Auto-Moderation",
  "tags": [],
  "nodes": [
    {
      "id": "f70bf3a1-798d-43fb-8c33-7411038c8ddb",
      "name": "Receive Facebook group",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2752,
        -1696
      ],
      "parameters": {
        "path": "fb-group-posts",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "3e60fd90-9425-4d28-a2d6-025706631e7b",
      "name": "Extracts Posts",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2512,
        -1696
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "body.entry[0].changes"
      },
      "typeVersion": 1
    },
    {
      "id": "37be9984-d487-48fc-83c3-b891626c98e2",
      "name": "Normalize post data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1920,
        -1680
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "732fd7b2-b206-4af9-97a9-3cc3a7cb7b74",
              "name": "post_id",
              "type": "string",
              "value": "={{ $json.value.post_id }}"
            },
            {
              "id": "4cf5cc1c-ec68-4711-9a68-c8614a1046ac",
              "name": "message",
              "type": "string",
              "value": "={{ $json.value.message }}"
            },
            {
              "id": "46d50acd-9c41-48ac-b32d-83cfd8bc2188",
              "name": "user_id",
              "type": "string",
              "value": "={{ $json.value.from.id }}"
            },
            {
              "id": "d7bd0231-4695-40ce-85e9-be773e1a8f65",
              "name": "user_name",
              "type": "string",
              "value": "={{ $json.value.from.name }}"
            },
            {
              "id": "fbfa656c-6c1d-425d-9955-69bd110cf87b",
              "name": "created_time",
              "type": "string",
              "value": "={{ $json.value.created_time }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "87fd0cb7-6022-402d-a692-af74e45be029",
      "name": "AI Content moderation",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -1552,
        -1744
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4-turbo",
          "cachedResultName": "GPT-4-TURBO"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=Group post content:\n\"{{$json.message}}\"\n"
            },
            {
              "content": "You are a Facebook Group moderation assistant.\n\nYour task:\n- Detect spam, scams, self-promotion, adult content, hate speech or policy violations.\n- Ignore normal discussion or genuine questions.\n- Be strict with promotional intent.\n\nReturn ONLY valid JSON in this format:\n{\n  \"violation\": true | false,\n  \"category\": \"spam | scam | adult | hate | self_promo | safe\",\n  \"severity\": \"low | medium | high\",\n  \"reason\": \"short explanation\"\n}\n"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4cbaedca-5219-49fb-b30a-6f1dec29b05b",
      "name": "Merge Ai result with post data",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1104,
        -1664
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "7f454043-5681-4076-95b7-5a3b3a8bce1c",
      "name": "Parse moderation result",
      "type": "n8n-nodes-base.code",
      "position": [
        -848,
        -1664
      ],
      "parameters": {
        "jsCode": "// Get both merged items\nconst items = $input.all();\n\n// AI response is FIRST item\nconst aiItem = items[0].json;\n\n// Post data is SECOND item\nconst postItem = items[1].json;\n\n// Parse AI JSON safely\nlet aiResult;\ntry {\n  const aiText = aiItem.output?.[0]?.content?.[0]?.text;\n  aiResult = JSON.parse(aiText);\n} catch (e) {\n  aiResult = {\n    violation: false,\n    category: \"safe\",\n    severity: \"low\",\n    reason: \"AI response parsing failed\"\n  };\n}\n\n// Merge AI + Post data\nreturn [\n  {\n    postId: postItem.post_id,\n    message: postItem.message,\n    userId: postItem.user_id,\n    userName: postItem.user_name,\n    createdTime: postItem.created_time,\n\n    violation: aiResult.violation,\n    category: aiResult.category,\n    severity: aiResult.severity,\n    reason: aiResult.reason\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b5bf00dd-ad12-4f4f-b2dc-0324be154d50",
      "name": "Violations?",
      "type": "n8n-nodes-base.if",
      "position": [
        -640,
        -1664
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f95a73c5-e663-4d3c-a403-84f151c5eeec",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.violation }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c24b1afa-1853-48d8-88f8-4410f7b4f921",
      "name": "Severity high?",
      "type": "n8n-nodes-base.if",
      "position": [
        -256,
        -1392
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "68d2321d-8d59-434a-9082-03efa9721794",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.severity }}",
              "rightValue": "=high"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fc59ac45-8fd7-472a-9d50-f22c7211fa55",
      "name": "Violation alert notify",
      "type": "n8n-nodes-base.slack",
      "position": [
        -272,
        -2016
      ],
      "parameters": {
        "text": "=*Facebook Group Violation*\nUser Name: {{ $json.userName }}\nUser Id:  {{ $json.userId }}\n\nPost: {{ $json.message }}\n\nCategory: {{ $json.category }}\nSeverity: {{ $json.severity }}\n\nReason: {{ $json.reason }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e23eba99-778c-45db-b8c3-f1b66ab64dd1",
      "name": "Log violations",
      "type": "n8n-nodes-base.airtable",
      "position": [
        64,
        -2016
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appF2iYPgVqqyXDC1",
          "cachedResultUrl": "https://airtable.com/appF2iYPgVqqyXDC1",
          "cachedResultName": "n8n Demo"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbl6Gc01H9MBzwNDg",
          "cachedResultUrl": "https://airtable.com/appF2iYPgVqqyXDC1/tbl6Gc01H9MBzwNDg",
          "cachedResultName": "FacebookGroup Moderation"
        },
        "columns": {
          "value": {
            "Reason": "={{ $('Violations?').item.json.reason }}",
            "Message": "={{ $('Violations?').item.json.message }}",
            "Post Id": "={{ $('Violations?').item.json.postId }}",
            "User Id": "={{ $('Violations?').item.json.userId }}",
            "Category": "={{ $('Violations?').item.json.category }}",
            "Severity": "={{ $('Violations?').item.json.severity }}",
            "User Name": "={{ $('Violations?').item.json.userName }}"
          },
          "schema": [
            {
              "id": "User Name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "User Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "User Id",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "User Id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post Id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Post Id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Message",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Message",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Severity",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reason",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "create"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "e5712bd4-5842-4bde-b15c-8f11326c4639",
      "name": "Auto Hide Success Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        592,
        -1216
      ],
      "parameters": {
        "text": "=*Post is Auto Hide*\n\nPost ID: {{$json.postId}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "cfa420f1-6298-4d5c-bbc0-231b2f9710d9",
      "name": "Auto hide failure alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        592,
        -1616
      ],
      "parameters": {
        "text": "=Auto-hide FAILED\n\nPost ID: {{$json.postId}}\nStatus Code: {{$json.statusCode}}\n\nPlease remove the post manually.\n",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "1ab102bd-7195-4af9-83c7-141bcb483417",
      "name": "Hide Facebook Post",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        64,
        -1408
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v19.0/{{$json.postId}}",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n  \"is_hidden\": true\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.FB_PAGE_ACCESS_TOKEN}}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "07e007ef-5806-47e2-908e-2b8da8d55333",
      "name": "Process Posts",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -2224,
        -1696
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "5fc706d9-09ff-4f68-ad7a-70ec47d64052",
      "name": "Hide Post Failed",
      "type": "n8n-nodes-base.if",
      "position": [
        320,
        -1408
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a57c3e32-b670-403d-bab9-b79f16df446a",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{$json.statusCode}} ",
              "rightValue": 200
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ed2076ea-e144-46d6-a918-074203d357d2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2832,
        -1792
      ],
      "parameters": {
        "color": 7,
        "height": 240,
        "content": "It receives new posts made in the Facebook group using a webhook."
      },
      "typeVersion": 1
    },
    {
      "id": "fc6b78b1-8bb8-43a6-9d50-e4e0fb1d371a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2576,
        -1792
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 240,
        "content": "This step separates each post so they can be processed one by one."
      },
      "typeVersion": 1
    },
    {
      "id": "d677ee0e-3914-42b8-a4de-d9d5f134f5a2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        -2016
      ],
      "parameters": {
        "color": 7,
        "width": 1856,
        "height": 640,
        "content": "## Post Moderation Process\nThis process automatically checks Facebook group posts one by one.\nEach post is cleaned and sent to AI for review.\nThe AI decides whether the post is safe or breaks group rules (spam, scam, etc.).\nIf a post violates rules, the system takes action and notifies moderators.\nSafe posts are ignored and the workflow continues with the next post."
      },
      "typeVersion": 1
    },
    {
      "id": "3bf7020e-0674-4f53-839f-015fc735e382",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        -2208
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 432,
        "content": "## Notify team & Log Data\n\n**Violation alert notify:** Sends a Slack alert to moderators\nwith post details and violation reason.\n\n**Log violations:**  Saves the violation details in Airtable\nfor reporting and future reference.\n\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "88e69fcb-4d5f-4591-bdb1-22a858ef500a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -400,
        -1712
      ],
      "parameters": {
        "color": 7,
        "width": 1232,
        "height": 672,
        "content": "## Automatic Post Hiding Process\n\nThis process handles serious rule-breaking posts.\nIf a post is marked as high severity, the system tries to hide it automatically on Facebook.\nAfter the request, the system checks whether the post was hidden successfully.\nThe team is then notified in Slack about the result \u2014 either success or failure.\nIf auto-hide fails, moderators are informed to take manual action."
      },
      "typeVersion": 1
    },
    {
      "id": "c1a7ae1e-bc95-43a2-ba7d-efba143221b4",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3504,
        -2336
      ],
      "parameters": {
        "width": 528,
        "height": 720,
        "content": "## How it works \nThis workflow automatically monitors posts made in a Facebook Group and helps moderators act quickly on harmful or unwanted content.\n\nWhen a new post appears, the workflow receives it and processes posts one by one. Each post is cleaned and prepared so important details like post ID, message, user name and time are easy to work with.\nThe post message is then sent to AI, which checks whether it violates group rules (such as spam, scams, hate, adult content or self-promotion).\n\nIf the post is safe, the workflow simply moves on to the next post.\nIf the AI flags a violation, the system alerts the moderation team and saves the details for tracking.\nFor high-severity violations, the workflow automatically hides the post on Facebook.\nFinally, the team is notified in Slack whether the post was hidden successfully or if manual action is needed.\n\nThis helps reduce manual moderation work and ensures fast responses to serious issues.\n\n## Setup steps\n\n**1.** Connect the Facebook Webhook to your group feed.\n\n**2.** Add a valid Facebook Page Access Token in environment variables.\n\n**3.** Configure OpenAI credentials for content moderation.\n\n**4.** Connect Slack for alerts and Airtable for logging.\n\n**5.** Test the workflow with sample Facebook posts.\n\n**6.** Activate the workflow."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1c320b31-c21b-4468-aa32-bf10850105c5",
  "connections": {
    "Violations?": {
      "main": [
        [
          {
            "node": "Violation alert notify",
            "type": "main",
            "index": 0
          },
          {
            "node": "Severity high?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Posts": {
      "main": [
        [],
        [
          {
            "node": "Normalize post data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extracts Posts": {
      "main": [
        [
          {
            "node": "Process Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log violations": {
      "main": [
        [
          {
            "node": "Process Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Severity high?": {
      "main": [
        [
          {
            "node": "Hide Facebook Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hide Post Failed": {
      "main": [
        [
          {
            "node": "Auto hide failure alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Auto Hide Success Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hide Facebook Post": {
      "main": [
        [
          {
            "node": "Hide Post Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize post data": {
      "main": [
        [
          {
            "node": "AI Content moderation",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Ai result with post data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "AI Content moderation": {
      "main": [
        [
          {
            "node": "Merge Ai result with post data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Facebook group": {
      "main": [
        [
          {
            "node": "Extracts Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Violation alert notify": {
      "main": [
        [
          {
            "node": "Log violations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse moderation result": {
      "main": [
        [
          {
            "node": "Violations?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Ai result with post data": {
      "main": [
        [
          {
            "node": "Parse moderation result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}