This workflow corresponds to n8n.io template #16391 — we link there as the canonical source.
This workflow follows the Emailsend → HTTP Request 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 →
{
"name": "PolyDoc - Website change monitor (screenshot on change)",
"nodes": [
{
"id": "d1111111-1111-4111-8111-111111111111",
"name": "Every 6 hours",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
240,
320
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"typeVersion": 1.2
},
{
"id": "d2222222-2222-4222-8222-222222222222",
"name": "Settings",
"type": "n8n-nodes-base.set",
"position": [
440,
320
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "set-url",
"name": "url",
"type": "string",
"value": "https://example.com"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d3333333-3333-4333-8333-333333333333",
"name": "Fetch page",
"type": "n8n-nodes-base.httpRequest",
"position": [
640,
320
],
"parameters": {
"url": "={{ $json.url }}",
"method": "GET",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "d4444444-4444-4444-8444-444444444444",
"name": "Detect change",
"type": "n8n-nodes-base.code",
"position": [
840,
320
],
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": " const res = $input.first().json;\n const html = res.data ?? (typeof res === 'string' ? res : JSON.stringify(res));\n const normalized = String(html)\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n\n // djb2-xor hash: no imports, works in the sandboxed Code node.\n let hash = 5381;\n for (let i = 0; i < normalized.length; i++) {\n hash = ((hash * 33) ^ normalized.charCodeAt(i)) >>> 0;\n }\n const current = String(hash);\n\n const store = $getWorkflowStaticData('global');\n const previous = store.lastHash;\n store.lastHash = current;\n\n return [\n {\n json: {\n url: $('Settings').first().json.url,\n changed: previous !== current,\n firstRun: previous === undefined,\n previousHash: previous ?? null,\n currentHash: current,\n },\n },\n ];"
},
"typeVersion": 2
},
{
"id": "d5555555-5555-4555-8555-555555555555",
"name": "Changed?",
"type": "n8n-nodes-base.if",
"position": [
1040,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-changed",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.changed }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "d6666666-6666-4666-8666-666666666666",
"name": "Screenshot page",
"type": "n8n-nodes-polydoc.polyDoc",
"position": [
1280,
320
],
"parameters": {
"url": "={{ $json.url }}",
"operation": "screenshot",
"sourceType": "url",
"deliveryMode": "download",
"additionalFields": {
"filename": "snapshot.png"
},
"screenshotOptions": {
"fullPage": true,
"imageType": "png",
"viewportWidth": 1280,
"viewportHeight": 800
},
"binaryPropertyName": "data"
},
"typeVersion": 1
},
{
"id": "d7777777-7777-4777-8777-777777777777",
"name": "Email screenshot",
"type": "n8n-nodes-base.emailSend",
"position": [
1480,
320
],
"parameters": {
"text": "=The watched page changed since the last check.\n\nURL: {{ $('Detect change').first().json.url }}\n\nThe current full-page screenshot is attached.",
"options": {
"attachments": "data"
},
"subject": "=Change detected: {{ $('Detect change').first().json.url }}",
"toEmail": "you@example.com",
"fromEmail": "user@example.com",
"emailFormat": "text"
},
"typeVersion": 2.1
},
{
"id": "d8888888-8888-4888-8888-888888888888",
"name": "No change",
"type": "n8n-nodes-base.noOp",
"position": [
1280,
700
],
"parameters": {},
"typeVersion": 1
},
{
"id": "d9999999-9999-4999-8999-999999999999",
"name": "Read me first",
"type": "n8n-nodes-base.stickyNote",
"position": [
200,
-260
],
"parameters": {
"width": 1380,
"height": 360,
"content": "## Website change monitor with screenshots\n\n**What it does:** Polls a web page on a schedule, detects whether its content changed since the last run, and captures a full-page PolyDoc screenshot **only when it changed**, so you never spend a screenshot on an unchanged page. The snapshot is emailed to you.\n\n**Who it's for:** Anyone tracking competitor pages, pricing, docs, or status pages who wants a visual record of every change.\n\n### Setup\n1. Set the **url** to watch in the *Settings* node.\n2. Add your **PolyDoc API** credential on *Screenshot page* (free key at dashboard.polydoc.tech).\n3. Add an **SMTP** credential on *Email screenshot*, or swap it for Slack / S3 / Google Drive.\n\nStart free: https://polydoc.tech"
},
"typeVersion": 1
},
{
"id": "da111111-1111-4111-8111-111111111111",
"name": "Section: Watch",
"type": "n8n-nodes-base.stickyNote",
"position": [
200,
180
],
"parameters": {
"color": 4,
"width": 400,
"height": 260,
"content": "## 1. Watch on a schedule\n\nRuns every 6 hours. The URL to watch lives in **Settings**, so you edit it in one place."
},
"typeVersion": 1
},
{
"id": "db222222-2222-4222-8222-222222222222",
"name": "Section: Detect",
"type": "n8n-nodes-base.stickyNote",
"position": [
620,
180
],
"parameters": {
"color": 4,
"width": 620,
"height": 260,
"content": "## 2. Detect a real change\n\nFetch the page, strip scripts, styles and whitespace, hash it, and compare to the last run (stored in workflow static data). Only a genuine content change continues."
},
"typeVersion": 1
},
{
"id": "dc333333-3333-4333-8333-333333333333",
"name": "Section: Capture",
"type": "n8n-nodes-base.stickyNote",
"position": [
1260,
180
],
"parameters": {
"color": 4,
"width": 420,
"height": 260,
"content": "## 3. Capture & send\n\nPolyDoc renders a full-page PNG of the changed page, which is emailed as an attachment."
},
"typeVersion": 1
},
{
"id": "de555555-5555-4555-8555-555555555555",
"name": "Setup: URL",
"type": "n8n-nodes-base.stickyNote",
"position": [
400,
470
],
"parameters": {
"color": 7,
"width": 180,
"height": 110,
"content": "**Set the URL** you want to watch here."
},
"typeVersion": 1
},
{
"id": "df666666-6666-4666-8666-666666666666",
"name": "Note: first run",
"type": "n8n-nodes-base.stickyNote",
"position": [
660,
470
],
"parameters": {
"color": 7,
"width": 380,
"height": 130,
"content": "First run and every manual test count as a change, so the screenshot branch runs right away. State then persists between scheduled runs."
},
"typeVersion": 1
},
{
"id": "e0777777-7777-4777-8777-777777777777",
"name": "Setup: PolyDoc key",
"type": "n8n-nodes-base.stickyNote",
"position": [
1240,
470
],
"parameters": {
"color": 7,
"width": 190,
"height": 110,
"content": "**Add your PolyDoc API credential.** Free key at dashboard.polydoc.tech"
},
"typeVersion": 1
},
{
"id": "e1888888-8888-4888-8888-888888888888",
"name": "Setup: email",
"type": "n8n-nodes-base.stickyNote",
"position": [
1450,
470
],
"parameters": {
"color": 7,
"width": 210,
"height": 110,
"content": "**Add an SMTP credential**, or swap this node for Slack / S3 / Google Drive."
},
"typeVersion": 1
},
{
"id": "e2999999-9999-4999-8999-999999999999",
"name": "Note: no change",
"type": "n8n-nodes-base.stickyNote",
"position": [
1420,
700
],
"parameters": {
"color": 7,
"width": 250,
"height": 110,
"content": "**Nothing changed.** No screenshot is taken, the run ends quietly."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"connections": {
"Changed?": {
"main": [
[
{
"node": "Screenshot page",
"type": "main",
"index": 0
}
],
[
{
"node": "No change",
"type": "main",
"index": 0
}
]
]
},
"Settings": {
"main": [
[
{
"node": "Fetch page",
"type": "main",
"index": 0
}
]
]
},
"Fetch page": {
"main": [
[
{
"node": "Detect change",
"type": "main",
"index": 0
}
]
]
},
"Detect change": {
"main": [
[
{
"node": "Changed?",
"type": "main",
"index": 0
}
]
]
},
"Every 6 hours": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"Screenshot page": {
"main": [
[
{
"node": "Email screenshot",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow checks a website URL every 6 hours, detects content changes by hashing the normalized HTML, and when a change is found it uses PolyDoc to capture a full-page screenshot and emails the PNG as an attachment. Runs every 6 hours on a schedule. Sets the target URL to…
Source: https://n8n.io/workflows/16391/ — 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.
Automatically monitor billable Kimai projects every weekday morning and receive a formatted HTML email when a project deadline is approaching or its hour budget is running low. If nothing requires att
Rank Drop Alert Monitor. Uses googleSheets, httpRequest, slack, emailSend. Scheduled trigger; 11 nodes.
Proxmox Infrastructure Monitoring. Uses httpRequest, emailSend. Scheduled trigger; 7 nodes.
AlphaAI: daily watchlist news digest -> email. Uses httpRequest, emailSend. Scheduled trigger; 6 nodes.
Uptime Alerting & Notification. Uses httpRequest, emailSend. Scheduled trigger; 4 nodes.