{
  "id": "mintapi-local-review-intelligence",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Generate local review digests from Google Maps with MintAPI and OpenAI",
  "tags": [
    {
      "name": "MintAPI"
    },
    {
      "name": "Local SEO"
    },
    {
      "name": "Reviews"
    }
  ],
  "nodes": [
    {
      "id": "e131eccc-0208-482b-975a-2d19bfdd8cfa",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1504,
        -176
      ],
      "parameters": {
        "width": 480,
        "height": 608,
        "content": "## Generate local review digests from Google Maps with MintAPI and OpenAI\n\n### How it works\n\n1. Triggers manually or on a weekly schedule. \n2. Sets search query and parameters for Google Maps businesses. \n3. Fetches business data and reviews via MintAPI. \n4. Processes reviews using OpenAI to generate digests. \n5. Compiles and sends email reports based on digests.\n\n### Setup steps\n\n- [ ] Configure credentials for MintAPI.\n- [ ] Configure credentials for OpenAI API.\n- [ ] Set email server for 'Send Email' node.\n\n### Customization\n\nAdjust search queries and parameters in 'Template Settings' to refine data."
      },
      "typeVersion": 1
    },
    {
      "id": "ca44c207-38e5-43fb-add1-b085c65e3a4e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 512,
        "content": "## Initiate Workflow\n\nTriggers workflow manually or on a weekly schedule."
      },
      "typeVersion": 1
    },
    {
      "id": "3de53fd0-12a1-4f26-a2c7-71255119be86",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## Setup and search\n\nSets search parameters and queries Google Maps businesses."
      },
      "typeVersion": 1
    },
    {
      "id": "2a4c73ab-49f5-4401-8ce2-806964a692a2",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## Process business data\n\nPrepares and fetches businesses reviews from MintAPI."
      },
      "typeVersion": 1
    },
    {
      "id": "dc59b936-cb0f-46a1-91c9-33c784970306",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## Generate and analyze\n\nBuilds prompt for OpenAI and generates review digest."
      },
      "typeVersion": 1
    },
    {
      "id": "56826add-3941-4082-9154-72db5ff9e8ac",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 272,
        "content": "## Prepare and send email\n\nCompiles email report and sends it via email."
      },
      "typeVersion": 1
    },
    {
      "id": "f24b2b5a-329e-48f8-a6e7-18db98e148fd",
      "name": "Manual Execution Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -900,
        180
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1e652282-dc38-45fb-8a68-5fe0933fde4a",
      "name": "When Monday at 9am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -900,
        -40
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ba97e075-c6d0-4d9e-9932-30bdbbd1f81f",
      "name": "Set Template Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        -620,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "search-query",
              "name": "searchQuery",
              "type": "string",
              "value": "med spas in Miami"
            },
            {
              "id": "region",
              "name": "region",
              "type": "string",
              "value": "us"
            },
            {
              "id": "language",
              "name": "language",
              "type": "string",
              "value": "en"
            },
            {
              "id": "max-businesses",
              "name": "maxBusinesses",
              "type": "number",
              "value": 5
            },
            {
              "id": "reviews-per-business",
              "name": "reviewsPerBusiness",
              "type": "number",
              "value": 20
            },
            {
              "id": "alert-keywords",
              "name": "alertKeywords",
              "type": "string",
              "value": "price,wait,service,rude,quality,clean,parking,appointment,staff,refund,late"
            },
            {
              "id": "recipient-email",
              "name": "recipientEmail",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "sender-email",
              "name": "senderEmail",
              "type": "string",
              "value": "user@example.com"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "be5e16e5-8f09-45c6-91ad-24184d750696",
      "name": "Fetch Google Maps Businesses",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -340,
        80
      ],
      "parameters": {
        "url": "https://api.mintapi.dev/api/google-maps/search",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ $json.searchQuery }}"
            },
            {
              "name": "limit",
              "value": "={{ $json.maxBusinesses }}"
            },
            {
              "name": "region",
              "value": "={{ $json.region }}"
            },
            {
              "name": "language",
              "value": "={{ $json.language }}"
            },
            {
              "name": "extract_emails_and_contacts",
              "value": "false"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "31702b38-a725-41e2-877c-3b4123fba6b7",
      "name": "Prepare Competitor Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -60,
        80
      ],
      "parameters": {
        "jsCode": "const settings = $items('Set Template Parameters')[0].json;\nconst response = $input.first().json;\nconst candidates = Array.isArray(response.data)\n  ? response.data\n  : Array.isArray(response.results)\n    ? response.results\n    : Array.isArray(response.businesses)\n      ? response.businesses\n      : [];\n\nreturn candidates\n  .filter((business) => business && (business.business_id || business.place_id))\n  .slice(0, Number(settings.maxBusinesses || 5))\n  .map((business) => ({\n    json: {\n      settings,\n      business,\n      businessId: business.business_id || business.place_id,\n      businessName: business.name || business.title || 'Unknown business',\n      rating: business.rating ?? null,\n      reviewCount: business.review_count ?? business.reviews_count ?? null,\n      address: business.full_address || business.address || '',\n      website: business.website || '',\n      phone: business.phone_number || business.phone || ''\n    }\n  }));"
      },
      "typeVersion": 2
    },
    {
      "id": "8d9a4cb5-54fe-4420-9c27-5a6eedf3d742",
      "name": "Fetch Latest Business Reviews",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        220,
        80
      ],
      "parameters": {
        "url": "https://api.mintapi.dev/api/google-maps/business-reviews",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "business_id",
              "value": "={{ $json.businessId }}"
            },
            {
              "name": "limit",
              "value": "={{ $json.settings.reviewsPerBusiness }}"
            },
            {
              "name": "sort_by",
              "value": "newest"
            },
            {
              "name": "region",
              "value": "={{ $json.settings.region }}"
            },
            {
              "name": "language",
              "value": "={{ $json.settings.language }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "64657ead-5204-46e6-8099-f87e4147c37d",
      "name": "Build AI Review Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        500,
        80
      ],
      "parameters": {
        "jsCode": "const settings = $items('Set Template Parameters')[0].json;\nconst competitors = $items('Prepare Competitor Data').map((item) => item.json);\nconst reviewResponses = $input.all().map((item) => item.json);\nconst keywords = String(settings.alertKeywords || '')\n  .split(',')\n  .map((keyword) => keyword.trim().toLowerCase())\n  .filter(Boolean);\n\nfunction getReviews(payload) {\n  if (Array.isArray(payload.data)) return payload.data;\n  if (Array.isArray(payload.reviews)) return payload.reviews;\n  if (payload.data && Array.isArray(payload.data.reviews)) return payload.data.reviews;\n  if (payload.result && Array.isArray(payload.result.reviews)) return payload.result.reviews;\n  return [];\n}\n\nfunction reviewText(review) {\n  return String(review.review_text || review.text || review.comment || review.snippet || '').trim();\n}\n\nfunction reviewRating(review) {\n  const raw = review.rating ?? review.review_rating ?? review.stars ?? review.score;\n  const parsed = Number(raw);\n  return Number.isFinite(parsed) ? parsed : null;\n}\n\nconst rows = competitors.map((competitor, index) => {\n  const reviews = getReviews(reviewResponses[index] || {});\n  const normalizedReviews = reviews.map((review) => {\n    const text = reviewText(review);\n    const lower = text.toLowerCase();\n    const rating = reviewRating(review);\n    const matchedKeywords = keywords.filter((keyword) => lower.includes(keyword));\n    return {\n      author: review.author_name || review.user_name || review.name || 'Anonymous',\n      rating,\n      text,\n      date: review.published_at || review.date || review.time || review.review_datetime_utc || '',\n      matchedKeywords\n    };\n  });\n\n  const importantReviews = normalizedReviews\n    .filter((review) => (review.rating !== null && review.rating <= 3) || review.matchedKeywords.length > 0)\n    .slice(0, 8);\n\n  return {\n    name: competitor.businessName,\n    rating: competitor.rating,\n    reviewCount: competitor.reviewCount,\n    address: competitor.address,\n    website: competitor.website,\n    phone: competitor.phone,\n    importantReviewCount: importantReviews.length,\n    importantReviews\n  };\n});\n\nconst prompt = `You are a local marketing analyst. Create a concise weekly review intelligence report for the search query \"${settings.searchQuery}\".\n\nUse only the data below. Focus on patterns, not generic advice.\n\nReturn Markdown with these sections:\n1. Executive Summary\n2. Competitor Snapshot\n3. Complaint Themes\n4. What Customers Praise\n5. Three Marketing Actions To Take This Week\n6. Review Response Drafts\n\nData:\n${JSON.stringify(rows, null, 2)}`;\n\nconst fallbackReport = [\n  `## Local Review Intelligence: ${settings.searchQuery}`,\n  '',\n  '## Competitor Snapshot',\n  ...rows.map((row) => `- ${row.name}: rating ${row.rating ?? 'n/a'}, reviews ${row.reviewCount ?? 'n/a'}, important reviews ${row.importantReviewCount}`),\n  '',\n  '## Important Reviews',\n  ...rows.flatMap((row) => [\n    `### ${row.name}`,\n    ...(row.importantReviews.length ? row.importantReviews.map((review) => `- ${review.rating ?? 'n/a'} stars: ${review.text}`) : ['- No important reviews matched the current filters.'])\n  ])\n].join('\\\\n');\n\nreturn [\n  {\n    json: {\n      settings,\n      searchQuery: settings.searchQuery,\n      competitors: rows,\n      openAiPrompt: prompt,\n      fallbackReport,\n      recipientEmail: settings.recipientEmail,\n      senderEmail: settings.senderEmail\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "85d639f6-a837-4933-8358-340a76153d2e",
      "name": "Generate Review Digest via OpenAI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        780,
        80
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ { model: 'gpt-4o-mini', messages: [{ role: 'system', content: 'You summarize local business reviews into concise local marketing intelligence.' }, { role: 'user', content: $json.openAiPrompt }], temperature: 0.2 } }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "4dad6d36-84e2-4820-94c9-a24c4c8c4017",
      "name": "Prepare Final Email Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1060,
        80
      ],
      "parameters": {
        "jsCode": "const source = $items('Build AI Review Prompt')[0].json;\nconst ai = $input.first().json;\nconst reportMarkdown = ai.choices?.[0]?.message?.content || source.fallbackReport;\n\nreturn [\n  {\n    json: {\n      ...source,\n      reportMarkdown,\n      emailSubject: `Local Review Intelligence: ${source.searchQuery}`\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "c1be7a54-1302-4f49-a2b6-ce0b677cc642",
      "name": "Send Final Email Report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        1340,
        80
      ],
      "parameters": {
        "text": "={{ $json.reportMarkdown }}",
        "options": {},
        "subject": "={{ $json.emailSubject }}",
        "toEmail": "={{ $json.recipientEmail }}",
        "fromEmail": "={{ $json.senderEmail }}",
        "emailFormat": "text"
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "3d9d4e16-9189-4ec5-b20c-c7b57b5e54a5",
  "connections": {
    "When Monday at 9am": {
      "main": [
        [
          {
            "node": "Set Template Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Review Prompt": {
      "main": [
        [
          {
            "node": "Generate Review Digest via OpenAI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Competitor Data": {
      "main": [
        [
          {
            "node": "Fetch Latest Business Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Template Parameters": {
      "main": [
        [
          {
            "node": "Fetch Google Maps Businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Execution Trigger": {
      "main": [
        [
          {
            "node": "Set Template Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Final Email Report": {
      "main": [
        [
          {
            "node": "Send Final Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Google Maps Businesses": {
      "main": [
        [
          {
            "node": "Prepare Competitor Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Latest Business Reviews": {
      "main": [
        [
          {
            "node": "Build AI Review Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Review Digest via OpenAI": {
      "main": [
        [
          {
            "node": "Prepare Final Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}