This workflow corresponds to n8n.io template #9893 — we link there as the canonical source.
This workflow follows the Google Sheets → Slack recipe pattern — see all workflows that pair these two integrations.
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 →
{
"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
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
browserActApigoogleSheetsOAuth2ApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow is essential for brand owners, manufacturers, and compliance teams who need to proactively monitor their distribution channels and enforce pricing policies.
Source: https://n8n.io/workflows/9893/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of
Enhance financial oversight with this automated n8n workflow. Triggered every 5 minutes, it fetches real-time bank transactions via an API, enriches and transforms the data, and applies smart logic to
This workflow automates competitive price intelligence using Bright Data's enterprise web scraping API. On a scheduled basis (default: daily at 9 AM), the system loops through configured competitor pr
Ensure your customer SLAs never slip with this n8n automation template. The workflow runs on a schedule, fetching open tickets from Zendesk, calculating SLA time remaining, and sending proactive alert
> n8n, Binance API, Google Sheets, Slack, Telegram, Jira & Email