{
  "name": "Weekly white-label AI visibility packets for every client (Apify agency reports)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "id": "4cd247e8-3265-5a36-ba2f-632a0e7d06e6",
      "name": "Every Monday 07:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "93b9d9f7-4c99-53d3-9b64-e8641ace911d",
              "name": "clientsCsv",
              "value": "client,domain,competitors,category,keywords\nHelp Scout,helpscout.com,Zendesk | zendesk.com; Freshdesk | freshworks.com; Intercom | intercom.com,help desk software,help desk software; shared inbox software\nPipedrive,pipedrive.com,HubSpot | hubspot.com; Salesforce | salesforce.com,CRM for small business,crm for small business",
              "type": "string"
            },
            {
              "id": "5381dabd-c24b-5a70-8f95-bd83685662ee",
              "name": "agencyName",
              "value": "Your Agency",
              "type": "string"
            },
            {
              "id": "ea340edf-8897-5e50-9cb7-c052ad3692d8",
              "name": "agencyLogoUrl",
              "value": "",
              "type": "string"
            },
            {
              "id": "fe22afff-5ec4-5e16-b90c-f6ff7cb6905d",
              "name": "accentColor",
              "value": "#1F4E79",
              "type": "string"
            },
            {
              "id": "b144381e-fa39-5026-ad8a-b7cb4bd724f7",
              "name": "bookId",
              "value": "your-agency-weekly",
              "type": "string"
            },
            {
              "id": "369b6bae-0489-596a-966c-431dc029dbd6",
              "name": "deliveryEmail",
              "value": "you@youragency.com",
              "type": "string"
            },
            {
              "id": "57275184-c9a3-530e-a49c-7a2369c6b0c6",
              "name": "maxSpendUsd",
              "value": 60,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "55a1af85-7c99-538b-b417-8105fade3d57",
      "name": "Client book and branding",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "resource": "Actors",
        "operation": "Run actor",
        "actorSource": "store",
        "actorId": {
          "__rl": true,
          "value": "kqQDQyEAS24QqqCnF",
          "mode": "id"
        },
        "actorInputMode": "json",
        "customBody": "={{ JSON.stringify({ clientsCsv: $json.clientsCsv, agencyName: $json.agencyName, agencyLogoUrl: $json.agencyLogoUrl || undefined, accentColor: $json.accentColor, bookId: $json.bookId, includePdf: true }) }}",
        "waitForFinish": false,
        "timeout": 3600,
        "memory": 1024,
        "maxTotalChargeUsd": "={{ $json.maxSpendUsd }}"
      },
      "id": "0ea60f08-976f-58e7-becb-87bed295ab25",
      "name": "Start client book run",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 2,
      "position": [
        440,
        300
      ],
      "credentials": {
        "apifyApi": {
          "id": "",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "amount": 4,
        "unit": "minutes"
      },
      "id": "fea40a16-b5c8-52ce-90dc-c26c0e4f0c82",
      "name": "Wait 4 minutes",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        660,
        300
      ],
      "webhookId": "c3ccf862-b859-5624-b81c-9dc0836d7a6d"
    },
    {
      "parameters": {
        "resource": "Actor runs",
        "operation": "Get run",
        "runId": {
          "__rl": true,
          "value": "={{ $('Start client book run').first().json.id }}",
          "mode": "id"
        }
      },
      "id": "cf2c3b25-9cc8-59e5-912f-666018cf2da9",
      "name": "Check run status",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 2,
      "position": [
        880,
        300
      ],
      "credentials": {
        "apifyApi": {
          "id": "",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "9d15c84c-ec4d-5e0a-b22f-6533b833d71d",
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "edbd77ab-6d28-59e4-a63f-4c6a309d1768",
      "name": "Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "eaa425ce-628e-5349-a3e3-b97ea0853c3e",
              "leftValue": "={{ ['READY', 'RUNNING'].includes($json.status) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "61822ffa-b60f-52a1-8fb2-590f9e6eaf98",
      "name": "Still running?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1100,
        520
      ]
    },
    {
      "parameters": {
        "errorMessage": "={{ 'Agency report run ' + $json.id + ' ended with status ' + $json.status + '. Open it in Apify Console: https://console.apify.com/actors/runs/' + $json.id }}"
      },
      "id": "9243f274-8fbc-51b3-ab0b-cd95ea9f947a",
      "name": "Run failed",
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        1320,
        620
      ]
    },
    {
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/key-value-stores/' + $json.defaultKeyValueStoreId + '/records/MANIFEST' }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "apifyApi",
        "options": {}
      },
      "id": "77f9eab6-f855-5b24-bde3-942d12f7d7c7",
      "name": "Get MANIFEST",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        200
      ],
      "credentials": {
        "apifyApi": {
          "id": "",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// One summary item for email / Slack, built from the run's MANIFEST.\nconst m = $input.first().json;\nconst book = $('Client book and branding').first().json;\nconst pk = (m.packets || []).filter(p => p.status === 'complete' || p.status === 'partial');\nconst fmt = v => (v === null || v === undefined) ? 'n/a' : (v > 0 ? '+' : '') + v;\n// Clients that lost the most visibility first (week 1 has no delta: sort by lowest visibility instead)\npk.sort((a, b) => ((a.visibilityDelta ?? a.visibilityScore) - (b.visibilityDelta ?? b.visibilityScore)));\nconst lines = pk.map(p => `- ${p.client} (${p.domain}): AI visibility ${p.visibilityScore}% (${p.visibilityDelta === null || p.visibilityDelta === undefined ? 'first week' : fmt(p.visibilityDelta) + ' pts vs last week'}), share of voice ${p.shareOfVoice}%. Next: ${p.topAction || '-'}${p.status === 'partial' ? ' [partial, not charged]' : ''}`);\nconst text = [\n  `${book.agencyName}: AI visibility packets, ${m.weekLabel}`,\n  `${m.complete} complete, ${m.partial} partial, ${m.skipped} skipped. Charged: $${m.totalCharged}.`,\n  '', ...lines, '', 'Packets (HTML + PDF) and PORTFOLIO.html are attached / in Drive.'\n].join('\\n');\nreturn [{ json: { subject: `${book.agencyName} AI visibility packets: ${m.weekLabel}`, text, deliveryEmail: book.deliveryEmail } }];"
      },
      "id": "abd371bd-d9ac-537e-8a04-edb4b3347f9c",
      "name": "Portfolio summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        100
      ]
    },
    {
      "parameters": {
        "jsCode": "// One item per file to download: PORTFOLIO.html + HTML and PDF packet for every delivered client.\nconst m = $input.first().json;\nconst done = (m.packets || []).filter(p => (p.status === 'complete' || p.status === 'partial') && p.reportUrl);\nif (!done.length) return [];\nconst base = done[0].reportUrl.split('/records/')[0] + '/records/';   // the run's key-value store\nconst files = [{ key: 'PORTFOLIO.html', url: base + 'PORTFOLIO.html' }];\nfor (const p of done) for (const u of [p.reportUrl, p.pdfUrl]) if (u) files.push({ key: u.split('/records/')[1], url: u });\nreturn files.map(f => ({ json: f }));"
      },
      "id": "da5997e4-e855-52e5-b76c-688fdb2d3812",
      "name": "List packet files",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "apifyApi",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          }
        }
      },
      "id": "86018d0f-3c9f-5501-b422-65ba6f9deb46",
      "name": "Download file",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1760,
        300
      ],
      "credentials": {
        "apifyApi": {
          "id": "",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "name": "={{ $('List packet files').item.json.key }}",
        "driveId": {
          "__rl": true,
          "value": "My Drive",
          "mode": "list",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "root",
          "mode": "list",
          "cachedResultName": "/ (Root folder)"
        },
        "options": {}
      },
      "id": "5f94f132-1dc9-5856-89f1-d3aa2e7ec004",
      "name": "Upload to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1980,
        200
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Gmail needs all files on one item: data -> attachment_0, attachment_1, ...\nconst binary = {}; const names = [];\n$input.all().forEach((it, i) => { if (it.binary && it.binary.data) { binary['attachment_' + i] = it.binary.data; names.push('attachment_' + i); } });\nconst s = $('Portfolio summary').first().json;\nreturn [{ json: { ...s, attachments: names.join(',') }, binary }];"
      },
      "id": "84465287-d35c-52b5-9d47-f532aa2d2139",
      "name": "Bundle attachments",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1980,
        400
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.deliveryEmail }}",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.text }}",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ $json.attachments }}"
              }
            ]
          }
        }
      },
      "id": "c8a0500b-b02e-59f9-aa8b-9321c03ab70f",
      "name": "Email packets",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2200,
        400
      ],
      "credentials": {
        "gmailOAuth2": {
          "id": "",
          "name": "Gmail account"
        }
      },
      "webhookId": "17d53f89-8d9f-50b3-b760-ad0a972e50a5"
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "#client-reporting",
          "mode": "name"
        },
        "text": "={{ $json.text }}",
        "otherOptions": {}
      },
      "id": "b3675b2b-c3fd-51f4-8bd6-db16fadfba27",
      "name": "Post summary to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1760,
        100
      ],
      "credentials": {
        "slackApi": {
          "id": "",
          "name": "Slack account"
        }
      },
      "webhookId": "b1ca2c8b-7550-5e75-9851-22d0036e2754"
    },
    {
      "parameters": {
        "content": "## Weekly AI visibility packets for your whole client book\nEvery Monday this runs **Agency AI Visibility Reports** on Apify for every client in your book, waits for it, then sends a portfolio summary to Slack and the white-label packets (HTML + PDF per client, plus PORTFOLIO.html) to Google Drive and email.\n\n**Setup (10 min)**\n1. Apify node (@apify/n8n-nodes-apify): add an *Apify account* credential with your API token (Apify Console > Settings > API & Integrations). The HTTP nodes reuse it.\n2. Edit **Client book and branding**: paste your client CSV (`client, domain, competitors, category or questions, keywords`), agency name, logo URL, color, delivery email, a max spend per run.\n3. Pick a Drive folder, connect Gmail and Slack. Delete any destination you don't need.\n4. Test once (first run: about 3 minutes for 3 clients), then activate.\n\n**Cost:** $12 per completed client packet (less for 20+ clients), billed by Apify. Same-week re-runs are free; partial packets are free. Keep the same `bookId` every week to get week-over-week change.",
        "height": 400,
        "width": 560,
        "color": 5
      },
      "id": "a1d3b02f-0763-561e-81f9-03ee5a79e948",
      "name": "Setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -260
      ]
    }
  ],
  "connections": {
    "Every Monday 07:00": {
      "main": [
        [
          {
            "node": "Client book and branding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Client book and branding": {
      "main": [
        [
          {
            "node": "Start client book run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start client book run": {
      "main": [
        [
          {
            "node": "Wait 4 minutes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 4 minutes": {
      "main": [
        [
          {
            "node": "Check run status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check run status": {
      "main": [
        [
          {
            "node": "Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Succeeded?": {
      "main": [
        [
          {
            "node": "Get MANIFEST",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Still running?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Still running?": {
      "main": [
        [
          {
            "node": "Wait 4 minutes",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get MANIFEST": {
      "main": [
        [
          {
            "node": "Portfolio summary",
            "type": "main",
            "index": 0
          },
          {
            "node": "List packet files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Portfolio summary": {
      "main": [
        [
          {
            "node": "Post summary to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List packet files": {
      "main": [
        [
          {
            "node": "Download file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download file": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          },
          {
            "node": "Bundle attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bundle attachments": {
      "main": [
        [
          {
            "node": "Email packets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}