AutomationFlowsWeb Scraping › Automated Review Responder (claude + Gpt)

Automated Review Responder (claude + Gpt)

Automated Review Responder (Claude + GPT). Uses httpRequest. Scheduled trigger; 14 nodes.

Cron / scheduled trigger★★★★☆ complexity14 nodesHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 14 Complexity: ★★★★☆ Added:

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": "Automated Review Responder (Claude + GPT)",
  "nodes": [
    {
      "id": "1",
      "name": "Cron Trigger",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        200,
        200
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "hour": "*",
              "minute": "*/5"
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "List Reviews - Google Business Profile",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        480,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "https://mybusiness.googleapis.com/v4/accounts/YOUR_ACCOUNT_ID/locations/YOUR_LOCATION_ID/reviews",
        "responseFormat": "json"
      },
      "credentials": {
        "httpHeaderAuth": "<your credential>"
      }
    },
    {
      "id": "3",
      "name": "Parse Reviews",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        720,
        200
      ],
      "parameters": {
        "functionCode": "// Flatten Google response into one review per item\nconst data = items[0].json || {};\nconst reviews = data.reviews || [];\nreturn reviews.map(r => {\n  const reviewId = r.name ? r.name.split('/').pop() : null;\n  const ratingRaw = r.starRating || r.rating || null;\n  let rating = null;\n  if (typeof ratingRaw === 'string') {\n    const m = ratingRaw.match(/\\d+/);\n    rating = m ? parseInt(m[0], 10) : null;\n  } else if (typeof ratingRaw === 'number') {\n    rating = ratingRaw;\n  }\n  return {\n    json: {\n      reviewId,\n      comment: r.comment || '',\n      rating,\n      reviewer: r.reviewer?.displayName || ''\n    }\n  };\n});"
      }
    },
    {
      "id": "4",
      "name": "Split Reviews",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 1,
      "position": [
        960,
        200
      ],
      "parameters": {
        "batchSize": 1
      }
    },
    {
      "id": "5",
      "name": "Check Processed",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1200,
        200
      ],
      "parameters": {
        "functionCode": "const item = items[0].json;\nconst staticData = getWorkflowStaticData('global');\nstaticData.processedReviews = staticData.processedReviews || {};\nconst already = staticData.processedReviews[item.reviewId] ? 'yes' : 'no';\nreturn [{ json: { ...item, alreadyProcessed: already } }];"
      }
    },
    {
      "id": "6",
      "name": "If Not Processed",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1440,
        200
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"alreadyProcessed\"]}}",
              "operation": "equal",
              "value2": "no"
            }
          ]
        }
      }
    },
    {
      "id": "7",
      "name": "If Positive (>=4)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1680,
        200
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"rating\"]}}",
              "operation": "greaterThanOrEqual",
              "value2": 4
            }
          ]
        }
      }
    },
    {
      "id": "8",
      "name": "OpenAI GPT Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1920,
        120
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "headerAuth",
        "sendBody": true,
        "jsonParameters": true,
        "body": {
          "model": "gpt-4o-mini",
          "messages": [
            {
              "role": "system",
              "content": "You write friendly replies to positive reviews. Thank warmly, keep it casual yet professional, under 80 words."
            },
            {
              "role": "user",
              "content": "Review: {{$json[\"comment\"]}} | Rating: {{$json[\"rating\"]}}"
            }
          ],
          "max_tokens": 200
        }
      },
      "credentials": {
        "httpHeaderAuth": "<your credential>"
      }
    },
    {
      "id": "9",
      "name": "Claude Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1920,
        280
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "headerAuth",
        "sendBody": true,
        "jsonParameters": true,
        "body": {
          "model": "claude-3-5-sonnet-20240620",
          "max_tokens": 350,
          "system": "You are a professional customer success manager writing replies to critical reviews. Apologize, acknowledge, invite them to continue privately. Under 120 words.",
          "messages": [
            {
              "role": "user",
              "content": "Review: {{$json[\"comment\"]}} | Rating: {{$json[\"rating\"]}}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": "<your credential>"
      }
    },
    {
      "id": "10",
      "name": "Merge Positive",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        2160,
        120
      ],
      "parameters": {
        "mode": "mergeByIndex"
      }
    },
    {
      "id": "11",
      "name": "Merge Negative",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        2160,
        300
      ],
      "parameters": {
        "mode": "mergeByIndex"
      }
    },
    {
      "id": "12",
      "name": "Normalize Reply",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        2440,
        200
      ],
      "parameters": {
        "functionCode": "const d = items[0].json;\nlet reply = '';\nif (d.choices?.[0]?.message?.content) reply = d.choices[0].message.content;\nif (d.content?.[0]?.text) reply = d.content[0].text;\nreturn [{ json: { reviewId: d.reviewId, replyText: reply } }];"
      }
    },
    {
      "id": "13",
      "name": "Post Reply - GBP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        2720,
        200
      ],
      "parameters": {
        "method": "POST",
        "url": "https://mybusiness.googleapis.com/v4/accounts/YOUR_ACCOUNT_ID/locations/YOUR_LOCATION_ID/reviews/{{$json[\"reviewId\"]}}/reply",
        "sendBody": true,
        "jsonParameters": true,
        "body": {
          "comment": "={{$json[\"replyText\"]}}"
        }
      },
      "credentials": {
        "httpHeaderAuth": "<your credential>"
      }
    },
    {
      "id": "14",
      "name": "Mark Processed",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        3000,
        200
      ],
      "parameters": {
        "functionCode": "const id = items[0].json.reviewId;\nif (id) {\n  const s = getWorkflowStaticData('global');\n  s.processedReviews = s.processedReviews || {};\n  s.processedReviews[id] = new Date().toISOString();\n}\nreturn items;"
      }
    }
  ],
  "connections": {
    "Cron Trigger": {
      "main": [
        [
          {
            "node": "List Reviews - Google Business Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Reviews - Google Business Profile": {
      "main": [
        [
          {
            "node": "Parse Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Reviews": {
      "main": [
        [
          {
            "node": "Split Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Reviews": {
      "main": [
        [
          {
            "node": "Check Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Processed": {
      "main": [
        [
          {
            "node": "If Not Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Not Processed": {
      "main": [
        [
          {
            "node": "If Positive (>=4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Positive (>=4)": {
      "main": [
        [
          {
            "node": "OpenAI GPT Reply",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Claude Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI GPT Reply": {
      "main": [
        [
          {
            "node": "Merge Positive",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Claude Reply": {
      "main": [
        [
          {
            "node": "Merge Negative",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Positive": {
      "main": [
        [
          {
            "node": "Normalize Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Negative": {
      "main": [
        [
          {
            "node": "Normalize Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Reply": {
      "main": [
        [
          {
            "node": "Post Reply - GBP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Reply - GBP": {
      "main": [
        [
          {
            "node": "Mark Processed",
            "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.

Pro

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

About this workflow

Automated Review Responder (Claude + GPT). Uses httpRequest. Scheduled trigger; 14 nodes.

Source: https://gist.github.com/sambinofrank15/43e63f1b67ad1f6f74c694acbb2ef358 — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.

Stop And Error, HTTP Request, Email Send +1
Web Scraping

This template runs two scheduled workflows to govern Microsoft Entra ID (Azure AD) guest accounts by detecting stale users via Microsoft Graph, staging deletions in SharePoint with a 72-hour window, n

Microsoft SharePoint, Microsoft Teams, Microsoft Entra +1
Web Scraping

Jira-Allure-Auto-Qa. Uses httpRequest, jira. Scheduled trigger; 68 nodes.

HTTP Request, Jira
Web Scraping

Spotify-Sync-Surrealdb-V1. Uses httpRequest, n8n-nodes-surrealdb, spotify. Scheduled trigger; 62 nodes.

HTTP Request, N8N Nodes Surrealdb, Spotify
Web Scraping

As n8n instances scale, teams often lose track of sub-workflows—who uses them, where they are referenced, and whether they can be safely updated. This leads to inefficiencies like unnecessary copies o

HTTP Request, n8n, N8N Trigger +1