{
  "id": "hiFrvAExHDHKN4oj",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Real-Time MAP Enforcement & Price Violation Alerts using BrowserAct & slack",
  "tags": [],
  "nodes": [
    {
      "id": "77a6048a-7f41-4228-8440-a44d94300287",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -608,
        -160
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5744d319-95f1-4ba8-8bb7-013a0adbd0bd",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -448,
        -160
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1yB7rZc-7Lb6gpzdXxgOiSWltHuAbQ3nnSdelOppJPGs/edit#gid=0",
          "cachedResultName": "Reselers Link"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1yB7rZc-7Lb6gpzdXxgOiSWltHuAbQ3nnSdelOppJPGs",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1yB7rZc-7Lb6gpzdXxgOiSWltHuAbQ3nnSdelOppJPGs/edit?usp=drivesdk",
          "cachedResultName": "MAP Violation Alerts "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f6f3c76f-d431-4dad-8ab1-98d73fedc1c6",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -272,
        -144
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "332e4a3f-c1d3-419a-9bfc-b27d432e88e5",
      "name": "Run a workflow task",
      "type": "n8n-nodes-browseract-workflows.browserAct",
      "position": [
        -64,
        -128
      ],
      "parameters": {
        "workflowId": "57030890989130618",
        "inputParameters": {
          "parameters": [
            {
              "name": "Target_Link",
              "value": "={{ $json.Reseller_URL }}"
            }
          ]
        },
        "additionalFields": {
          "saveBrowserData": false
        }
      },
      "credentials": {
        "browserActApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2acfb998-c3c3-4078-a80a-8ad3c76326e2",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        688,
        0
      ],
      "parameters": {
        "options": {
          "ignoreCase": true
        },
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "41ecfaab-dc9e-4ac8-886d-01d40aac5f18",
              "operator": {
                "type": "number",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.Price }}",
              "rightValue": ""
            },
            {
              "id": "60daa8d6-e2ea-4de2-a98d-24039f659cd3",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.Price }}",
              "rightValue": "={{ $('Loop Over Items').item.json.AP_Price }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "2171e108-7894-46c8-8f82-3f69279e7800",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        816,
        -80
      ],
      "parameters": {
        "text": "={{ $('Loop Over Items').item.json.Reseller_Name }} is Breaking Rules\n{{ $('Loop Over Items').item.json.Product_SKU }} Price is less than {{ $('Loop Over Items').item.json.AP_Price }}\ncurrent Price : {{ $json.Price }}\nLink : {{ $('Loop Over Items').item.json.Reseller_URL }}\nDate : {{ $('Schedule Trigger').item.json[\"Readable date\"] }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09KLV9DJSX",
          "cachedResultName": "all-browseract-workflow-test"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "32d71b0c-eb59-4b3c-a343-555346874123",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1072,
        -80
      ],
      "parameters": {
        "mode": "chooseBranch",
        "output": "empty",
        "numberInputs": 3
      },
      "typeVersion": 3.2
    },
    {
      "id": "864840e9-5afc-41ed-879b-ec4ec5b57879",
      "name": "Get details of a workflow task",
      "type": "n8n-nodes-browseract-workflows.browserAct",
      "position": [
        128,
        -128
      ],
      "parameters": {
        "taskId": "={{ $json.id }}",
        "operation": "getTask",
        "waitForFinish": true
      },
      "credentials": {
        "browserActApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "acaf7b39-3fc5-4033-85e6-7b5fe4b8bb04",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        304,
        -128
      ],
      "parameters": {
        "jsCode": "// Get the JSON string using the exact path provided by the user.\nconst jsonString = $input.first().json.output.string;\n\nlet parsedData;\n\n// Check if the string exists before attempting to parse\nif (!jsonString) {\n    // Return an empty array or throw an error if no string is found\n    // Throwing an error is usually better to stop the workflow if data is missing.\n    throw new Error(\"Input string is empty or missing at the specified path: $input.first().json.output.string\");\n}\n\ntry {\n    // 1. Parse the JSON string into a JavaScript array of objects\n    parsedData = JSON.parse(jsonString);\n} catch (error) {\n    // Handle JSON parsing errors (e.g., if the string is malformed)\n    throw new Error(`Failed to parse JSON string: ${error.message}`);\n}\n\n// 2. Ensure the parsed data is an array\nif (!Array.isArray(parsedData)) {\n    throw new Error('Parsed data is not an array. It cannot be split into multiple items.');\n}\n\n// 3. Map the array of objects into the n8n item format { json: object }\n// Each element in this array will be treated as a new item by n8n, achieving the split.\nconst outputItems = parsedData.map(item => ({\n    json: item,\n}));\n\n// 4. Return the new array of items\nreturn outputItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "f6a2664e-bc1e-4c73-932d-1432f679e316",
      "name": "If1",
      "type": "n8n-nodes-base.if",
      "position": [
        480,
        -128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7aa4e4c0-02e1-4d50-86a7-5d58f4db2552",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Price }}",
              "rightValue": "NoData"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "47f04366-a59a-43d4-a926-9939952a5a15",
      "name": "Send a message1",
      "type": "n8n-nodes-base.slack",
      "position": [
        688,
        -160
      ],
      "parameters": {
        "text": "={{ $('Loop Over Items').item.json.Reseller_Name }} is out of stock on Product\n{{ $('Loop Over Items').item.json.Product_SKU }} \nLink : {{ $('Loop Over Items').item.json.Reseller_URL }}\nDate : {{ $('Schedule Trigger').item.json[\"Readable date\"] }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09KLV9DJSX",
          "cachedResultName": "all-browseract-workflow-test"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "eeb379b9-3e75-4160-a7f4-1754962667c4",
      "name": "Sticky Note - Intro",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1104,
        -464
      ],
      "parameters": {
        "width": 480,
        "height": 450,
        "content": "## Try It Out!\n### This n8n template automates MAP (Minimum Advertised Price) enforcement by monitoring reseller websites and alerting you to violations.\n\n### How it works\n* The workflow runs on a **schedule** (e.g., hourly) to proactively check prices.\n* It reads a list of your resellers, their product URLs, and the approved MAP price from a **Google Sheet**.\n* It then **loops** through each reseller one by one.\n* A **BrowserAct** node scrapes the current price from the live product page.\n* A series of **If** nodes check for violations: Is the price too low, or is the product out of stock?\n* If a violation is found, a specific, detailed **Slack** alert is sent to your team for immediate action.\n\n### Requirements\n* **BrowserAct** API account for web scraping.\n* **BrowserAct** n8n Community Node -> ([n8n Nodes BrowserAct](https://www.npmjs.com/package/n8n-nodes-browseract-workflows))\n* **Google Sheets** credentials for your price list.\n* **Slack** credentials for sending alerts.\n* A BrowserAct template named **\u201cMAP (Minimum Advertised Price) Violation Alerts\n\u201d**\n### Need Help?\nJoin the [Discord](https://discord.com/invite/UpnCKd7GaU) or Visit Our [Blog](https://www.browseract.com/blog)!\n"
      },
      "typeVersion": 1
    },
    {
      "id": "01d5f0f3-38b5-4946-aaf8-460ba02e53e4",
      "name": "Sticky Note - How to Use",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1104,
        0
      ],
      "parameters": {
        "width": 480,
        "height": 260,
        "content": "## How to use\n\n1.  **Set up Credentials:** Add your credentials for **BrowserAct**, **Google Sheets**, and **Slack** to the workflow.\n2.  **Set up BrowserAct Template:** Ensure you Use the **\u201cMAP (Minimum Advertised Price) Violation Alerts\n\u201d** template in your BrowserAct account.\n3.  **Prepare Your Google Sheet:** Your sheet must contain columns for `Reseller_URL`, `Reseller_Name`, `Product_SKU`, and the official MAP price (e.g., `AP_Price`).\n4.  **Set Slack Channel:** Update the **Channel ID** in **BOTH** Slack nodes to your desired alerts channel.\n5.  **Activate Workflow:** Activate the workflow to begin monitoring. You can change the frequency in the **Schedule Trigger** node."
      },
      "typeVersion": 1
    },
    {
      "id": "e60180af-3931-4a50-b7d7-02364ec06824",
      "name": "Sticky Note - Need Help",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1104,
        272
      ],
      "parameters": {
        "width": 480,
        "height": 152,
        "content": "### Need Help?\n* #### [How to Find Your BrowseAct API Key & Workflow ID](https://www.youtube.com/watch?v=pDjoZWEsZlE)\n* #### [How to Connect n8n to Browseract](https://www.youtube.com/watch?v=RoYMdJaRdcQ)\n* #### [How to Use & Customize BrowserAct Templates](https://www.youtube.com/watch?v=CPZHFUASncY)\n* #### [How to Use the BrowserAct N8N Community Node](https://youtu.be/j0Nlba2pRLU)\n* #### [I Built a Bot to Catch MAP Violators (n8n + BrowserAct Workflow)](https://youtu.be/ys6OZ7W3oww)"
      },
      "typeVersion": 1
    },
    {
      "id": "26b0718c-12da-4f57-b320-b3ae452dd6d1",
      "name": "Sticky Note - Input & Loop",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 152,
        "content": "### \ud83d\udccb 1. Input & Loop\n\n* **Schedule Trigger:** Kicks off the entire process automatically.\n* **Google Sheets:** This node fetches your master list of resellers and products to be monitored.\n* **Loop Over Items:** This is essential. It ensures the workflow processes each reseller from your sheet individually, preventing data mix-ups."
      },
      "typeVersion": 1
    },
    {
      "id": "f35d406d-ffb3-4476-a522-10292ade74c8",
      "name": "Sticky Note - Data Collection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -400
      ],
      "parameters": {
        "color": 5,
        "width": 528,
        "height": 168,
        "content": "### \ud83e\udd16 2. Price Scraping\n\nThis is the core data collection part of the workflow for each reseller in the loop.\n\n* **BrowserAct Nodes:** This pair of nodes navigates to the reseller's product page and scrapes the current price.\n* **Code Node:** This crucial step parses the output from the scraper, making the `Price` data clean and ready for the logic checks that follow."
      },
      "typeVersion": 1
    },
    {
      "id": "40221cb1-187e-4c53-a50a-0754a753fb95",
      "name": "Sticky Note - Logic & Alerting",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        -400
      ],
      "parameters": {
        "color": 3,
        "width": 752,
        "height": 176,
        "content": "### \u2696\ufe0f 3. Violation Logic & Alerting\n\nThis section analyzes the scraped data and takes action.\n\n* **If1 (Out of Stock Check):** The first `If` node checks if the scraper returned 'NoData'. This is a smart way to detect if a reseller is out of stock, sending a specific alert.\n* **If (Price Check):** The second `If` node compares the scraped `Price` against your `AP_Price` from the Google Sheet. If it's lower, it triggers the MAP violation alert.\n* **Slack Nodes:** Each condition has its own tailored Slack message, providing clear, actionable alerts to your team."
      },
      "typeVersion": 1
    },
    {
      "id": "75ab0a4d-9846-446c-8416-1210178ab93d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -208
      ],
      "parameters": {
        "color": 5,
        "width": 528,
        "height": 256,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "f975d0d3-ab49-43a5-8bfd-e29f54e6c135",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -208
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 256,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "076dd547-e5e3-44a9-a8ce-d01edde6bb46",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        -208
      ],
      "parameters": {
        "color": 3,
        "width": 752,
        "height": 320,
        "content": ""
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "f0b3fc52-a4f1-4e00-95f8-b1e23f9f878a",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Run a workflow task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run a workflow task": {
      "main": [
        [
          {
            "node": "Get details of a workflow task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get details of a workflow task": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}