{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "White-Label Client Ad Performance Report for Any Platform with Claude",
  "tags": [],
  "nodes": [
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000001",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        48
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## White-Label Client Ad Performance Report for Any Platform with Claude\n\n### How it works\n\nThis workflow runs on a schedule, fetches performance data from any single ad platform through one configurable HTTP Request, and normalizes the response into a standard metric set using broad field matching that already covers Google, Meta, LinkedIn, TikTok, and Microsoft responses. It splits the date range into a current and prior period to compute period-over-period deltas, then sends the numbers to Claude for a plain-English, client-friendly summary. A Code node assembles a branded HTML report using your agency name, logo, and brand color, emails it to the client, and appends a row to a Google Sheets log.\n\n### Setup steps\n\n- Edit the Set Branding and Report Config node with your agency name, logo URL, brand color, client name, platform name, recipient, and reporting period.\n- Point the Fetch Platform Metrics node at your ad platform's reporting endpoint and attach an HTTP Header Auth credential for it.\n- Configure an HTTP Header Auth credential for Anthropic with header name x-api-key and value sk-ant-... and attach it to the Generate Client Summary node.\n- Connect Gmail and Google Sheets credentials.\n- Create a Google Sheet with a tab named Client Reports and replace the placeholder sheet ID in Append Report to Log.\n- Confirm the field mapping in Normalize Metrics matches your platform's response, then adjust the schedule cadence to match your reporting cycle.\n\n### Customization\n\nReport on multiple platforms by adding more fetch nodes and merging them in Normalize Metrics. Swap Anthropic Claude for OpenAI, Groq, or a local model by editing the URL and body in Generate Client Summary. Replace Google Sheets with Airtable or Notion, deliver through Slack instead of Gmail, or loop the config over several clients with a Split In Batches node so one run sends a separate branded report per account."
      },
      "typeVersion": 1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000002",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Schedule and brand\n\nRuns on a reporting cadence and holds every white-label value: agency name, logo, brand color, client name, platform name, period label, and recipient."
      },
      "typeVersion": 1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000003",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Fetch and normalize\n\nPulls performance data from one configurable platform endpoint, then maps the response into a standard metric set and splits the window into current and prior periods for deltas."
      },
      "typeVersion": 1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000004",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 336,
        "content": "## Generate client summary\n\nSends the metrics and deltas to Claude with a client-facing rubric that returns a plain-English summary, highlights, watch items, and next steps."
      },
      "typeVersion": 1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000005",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1320,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 576,
        "content": "## Build and deliver branded report\n\nAssembles a white-label HTML report with your logo and brand color, emails it to the client, and appends a flat row to the Google Sheets log for record keeping."
      },
      "typeVersion": 1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000006",
      "name": "Run Monthly Schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        240,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtHour": 8,
              "triggerAtMinute": 0,
              "triggerAtDayOfMonth": 1
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000007",
      "name": "Set Branding and Report Config",
      "type": "n8n-nodes-base.set",
      "position": [
        460,
        320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b1-agency",
              "name": "agencyName",
              "type": "string",
              "value": "REPLACE_WITH_YOUR_AGENCY_NAME"
            },
            {
              "id": "b2-logo",
              "name": "agencyLogoUrl",
              "type": "string",
              "value": "REPLACE_WITH_YOUR_LOGO_URL"
            },
            {
              "id": "b3-color",
              "name": "brandColorHex",
              "type": "string",
              "value": "#1A73E8"
            },
            {
              "id": "b4-client",
              "name": "clientName",
              "type": "string",
              "value": "REPLACE_WITH_CLIENT_NAME"
            },
            {
              "id": "b5-platform",
              "name": "platformName",
              "type": "string",
              "value": "REPLACE_WITH_PLATFORM_NAME"
            },
            {
              "id": "b6-period",
              "name": "reportingPeriodLabel",
              "type": "string",
              "value": "={{ $now.minus({ months: 1 }).toFormat('LLLL yyyy') }}"
            },
            {
              "id": "b7-recipient",
              "name": "recipientEmail",
              "type": "string",
              "value": "client@example.com"
            },
            {
              "id": "b8-currency",
              "name": "currencySymbol",
              "type": "string",
              "value": "$"
            },
            {
              "id": "b9-date",
              "name": "reportDate",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000008",
      "name": "Fetch Platform Metrics",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        680,
        320
      ],
      "parameters": {
        "url": "REPLACE_WITH_YOUR_PLATFORM_REPORTING_ENDPOINT",
        "method": "GET",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000009",
      "name": "Normalize Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        900,
        320
      ],
      "parameters": {
        "jsCode": "// Normalize any ad-platform response into a standard metric set, then split into current vs prior period.\n// Field matching below already covers Google Ads, Meta, LinkedIn, TikTok, and Microsoft response shapes.\nconst config = $('Set Branding and Report Config').item.json;\nconst payload = $json;\n\n// Find the array of daily/row records wherever the platform put it\nfunction findRows(p) {\n  if (Array.isArray(p)) {\n    if (p.length && (p[0].results || p[0].data)) return p.flatMap(s => s.results || s.data || []);\n    return p;\n  }\n  return p.results || p.data || p.rows || (p.report && p.report.rows) || [];\n}\n\n// Read a value by trying several possible field paths; supports dot notation\nfunction getPath(obj, path) {\n  return path.split('.').reduce((o, k) => (o == null ? undefined : o[k]), obj);\n}\nfunction pick(row, keys) {\n  for (const k of keys) {\n    let v = getPath(row, k);\n    if (v !== undefined && v !== null && v !== '') {\n      let num = Number(v);\n      if (!isNaN(num)) {\n        if (/micros/i.test(k)) num = num / 1000000; // Google returns cost in micros\n        return num;\n      }\n    }\n  }\n  return 0;\n}\nfunction pickDate(row) {\n  const keys = ['date', 'segments.date', 'date_start', 'day', 'reportDate', 'stat_time_day'];\n  for (const k of keys) {\n    const v = getPath(row, k);\n    if (v) return String(v);\n  }\n  return '';\n}\n\nconst SPEND = ['spend', 'cost', 'metrics.cost', 'metrics.costMicros', 'cost_micros', 'amount_spent', 'spend_micros'];\nconst CLICKS = ['clicks', 'metrics.clicks', 'link_clicks', 'inline_link_clicks'];\nconst IMPR = ['impressions', 'metrics.impressions', 'impr', 'show_cnt'];\nconst CONV = ['conversions', 'metrics.conversions', 'results', 'leads', 'total_conversions', 'purchases', 'conversion'];\nconst REV = ['revenue', 'conversions_value', 'metrics.conversionsValue', 'conversion_value', 'purchase_value', 'total_revenue'];\n\nfunction sumRows(rows) {\n  let spend = 0, clicks = 0, impressions = 0, conversions = 0, revenue = 0;\n  for (const r of rows) {\n    spend += pick(r, SPEND);\n    clicks += pick(r, CLICKS);\n    impressions += pick(r, IMPR);\n    conversions += pick(r, CONV);\n    revenue += pick(r, REV);\n  }\n  const ctr = impressions > 0 ? (clicks / impressions) * 100 : 0;\n  const cpa = conversions > 0 ? spend / conversions : 0;\n  const cpc = clicks > 0 ? spend / clicks : 0;\n  const roas = spend > 0 ? revenue / spend : 0;\n  return { spend, clicks, impressions, conversions, revenue, ctr, cpa, cpc, roas };\n}\n\nfunction delta(curr, prev) {\n  const pct = (c, p) => (p > 0 ? ((c - p) / p) * 100 : 0);\n  return {\n    spend_pct: pct(curr.spend, prev.spend),\n    clicks_pct: pct(curr.clicks, prev.clicks),\n    impressions_pct: pct(curr.impressions, prev.impressions),\n    conversions_pct: pct(curr.conversions, prev.conversions),\n    cpa_pct: pct(curr.cpa, prev.cpa),\n    ctr_pp: curr.ctr - prev.ctr,\n    roas_pct: pct(curr.roas, prev.roas)\n  };\n}\n\nlet rows = findRows(payload);\nif (!Array.isArray(rows)) rows = [];\nconst sorted = rows.slice().sort((a, b) => pickDate(a).localeCompare(pickDate(b)));\nconst half = Math.floor(sorted.length / 2);\nconst prevRows = half > 0 ? sorted.slice(0, half) : [];\nconst currRows = half > 0 ? sorted.slice(half) : sorted;\n\nconst current = sumRows(currRows);\nconst previous = sumRows(prevRows);\n\nreturn [{\n  json: {\n    config,\n    rowsFound: rows.length,\n    metrics: { current, previous, delta: delta(current, previous) }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000010",
      "name": "Generate Client Summary",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1120,
        320
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-6\",\n  \"max_tokens\": 1200,\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"You are a senior account manager at a marketing agency writing a monthly performance summary for a client who is not a marketing expert. Reply with valid JSON only, no markdown fences, no commentary. Use this exact schema:\\n\\n{\\n  \\\"executive_summary\\\": \\\"<two to three plain-English sentences a business owner can understand>\\\",\\n  \\\"highlights\\\": [\\\"<positive result 1>\\\", \\\"<positive result 2>\\\", \\\"<positive result 3>\\\"],\\n  \\\"watch_items\\\": [\\\"<thing to keep an eye on 1>\\\", \\\"<thing to keep an eye on 2>\\\"],\\n  \\\"next_steps\\\": [\\\"<what the agency will do next 1>\\\", \\\"<2>\\\", \\\"<3>\\\"]\\n}\\n\\nRules:\\n- Write for a client, not a marketer. Avoid jargon; explain CPA as cost per result and ROAS as return on ad spend when you use them.\\n- Every highlight and watch item must reference a specific number from the data.\\n- Do not invent numbers. If a metric is zero or missing, treat it as data not yet available rather than a real value.\\n- Next steps must be concrete agency actions (test new creative, shift budget to the top campaign, expand the best audience), not vague.\\n- Keep each bullet to one sentence under 25 words.\\n- The platform being reported is \" + {{ JSON.stringify($json.config.platformName) }} + \" for the period \" + {{ JSON.stringify($json.config.reportingPeriodLabel) }} + \".\\n\\nDATA:\\n\" + {{ JSON.stringify($json.metrics) }}\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000011",
      "name": "Build Branded Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        320
      ],
      "parameters": {
        "jsCode": "// Parse Claude JSON, build the white-label HTML report, and flatten fields for the Sheets log\nconst input = $('Normalize Metrics').item.json;\nconst config = input.config;\nconst m = input.metrics;\nconst raw = $json.content?.[0]?.text || '{}';\n\nlet parsed = {\n  executive_summary: 'AI summary generation failed; the metrics below are still accurate.',\n  highlights: [],\n  watch_items: [],\n  next_steps: []\n};\ntry {\n  const cleaned = raw.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```\\s*$/i, '').trim();\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  parsed.executive_summary = 'AI returned malformed JSON: ' + raw.slice(0, 200);\n}\n\nconst cur = config.currencySymbol || '$';\nfunction money(n) { return cur + (Number(n) || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }\nfunction num(n) { return Math.round(Number(n) || 0).toLocaleString(); }\nfunction pct(n) { return (Number(n) || 0).toFixed(2) + '%'; }\nfunction dlt(n) { const v = Number(n) || 0; const s = v >= 0 ? '+' : ''; const c = v >= 0 ? '#137333' : '#b00020'; return '<span style=\"color:' + c + ';\">' + s + v.toFixed(1) + '%</span>'; }\nfunction li(arr) { return (Array.isArray(arr) ? arr : []).map(x => '<li>' + x + '</li>').join(''); }\n\nconst brand = config.brandColorHex || '#1A73E8';\nconst logo = config.agencyLogoUrl && !/^REPLACE_WITH/.test(config.agencyLogoUrl) ? '<img src=\"' + config.agencyLogoUrl + '\" alt=\"' + config.agencyName + '\" style=\"max-height:48px;\">' : '<span style=\"font-size:20px;font-weight:700;color:' + brand + ';\">' + config.agencyName + '</span>';\n\nconst html = '<div style=\"font-family:Arial,Helvetica,sans-serif;max-width:680px;margin:auto;color:#202124;\">' +\n  '<div style=\"border-bottom:3px solid ' + brand + ';padding:16px 0;\">' + logo + '</div>' +\n  '<h1 style=\"color:' + brand + ';font-size:22px;margin:20px 0 4px;\">' + config.platformName + ' Performance Report</h1>' +\n  '<p style=\"margin:0 0 20px;color:#5f6368;\">Prepared for ' + config.clientName + ' &middot; ' + config.reportingPeriodLabel + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.5;\">' + parsed.executive_summary + '</p>' +\n  '<table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse:collapse;margin:18px 0;font-size:14px;\">' +\n  '<tr style=\"background:' + brand + ';color:#fff;\"><th align=\"left\">Metric</th><th align=\"right\">This period</th><th align=\"right\">vs prior</th></tr>' +\n  '<tr><td>Spend</td><td align=\"right\">' + money(m.current.spend) + '</td><td align=\"right\">' + dlt(m.delta.spend_pct) + '</td></tr>' +\n  '<tr style=\"background:#f8f9fa;\"><td>Impressions</td><td align=\"right\">' + num(m.current.impressions) + '</td><td align=\"right\">' + dlt(m.delta.impressions_pct) + '</td></tr>' +\n  '<tr><td>Clicks</td><td align=\"right\">' + num(m.current.clicks) + '</td><td align=\"right\">' + dlt(m.delta.clicks_pct) + '</td></tr>' +\n  '<tr style=\"background:#f8f9fa;\"><td>Click-through rate</td><td align=\"right\">' + pct(m.current.ctr) + '</td><td align=\"right\">' + dlt(m.delta.ctr_pp) + '</td></tr>' +\n  '<tr><td>Results (conversions)</td><td align=\"right\">' + num(m.current.conversions) + '</td><td align=\"right\">' + dlt(m.delta.conversions_pct) + '</td></tr>' +\n  '<tr style=\"background:#f8f9fa;\"><td>Cost per result</td><td align=\"right\">' + money(m.current.cpa) + '</td><td align=\"right\">' + dlt(m.delta.cpa_pct) + '</td></tr>' +\n  (m.current.roas > 0 ? '<tr><td>Return on ad spend</td><td align=\"right\">' + (Number(m.current.roas) || 0).toFixed(2) + 'x</td><td align=\"right\">' + dlt(m.delta.roas_pct) + '</td></tr>' : '') +\n  '</table>' +\n  '<h3 style=\"color:' + brand + ';font-size:16px;\">Highlights</h3><ul>' + li(parsed.highlights) + '</ul>' +\n  '<h3 style=\"color:' + brand + ';font-size:16px;\">What we are watching</h3><ul>' + li(parsed.watch_items) + '</ul>' +\n  '<h3 style=\"color:' + brand + ';font-size:16px;\">Next steps</h3><ul>' + li(parsed.next_steps) + '</ul>' +\n  '<p style=\"margin-top:24px;padding-top:12px;border-top:1px solid #dadce0;color:#5f6368;font-size:12px;\">Prepared by ' + config.agencyName + '. Figures reflect ' + input.rowsFound + ' rows pulled from ' + config.platformName + '.</p>' +\n  '</div>';\n\nreturn [{\n  json: {\n    reportDate: config.reportDate,\n    agencyName: config.agencyName,\n    clientName: config.clientName,\n    platformName: config.platformName,\n    reportingPeriodLabel: config.reportingPeriodLabel,\n    recipientEmail: config.recipientEmail,\n    subject: config.platformName + ' report for ' + config.clientName + ' - ' + config.reportingPeriodLabel,\n    emailHtml: html,\n    spend: m.current.spend,\n    impressions: m.current.impressions,\n    clicks: m.current.clicks,\n    ctr: m.current.ctr,\n    conversions: m.current.conversions,\n    cpa: m.current.cpa,\n    roas: m.current.roas,\n    spend_delta_pct: m.delta.spend_pct,\n    conversions_delta_pct: m.delta.conversions_pct,\n    executive_summary: parsed.executive_summary,\n    highlights: (parsed.highlights || []).join(' | '),\n    watch_items: (parsed.watch_items || []).join(' | '),\n    next_steps: (parsed.next_steps || []).join(' | ')\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000012",
      "name": "Send Branded Report Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1580,
        208
      ],
      "parameters": {
        "sendTo": "={{ $json.recipientEmail }}",
        "message": "={{ $json.emailHtml }}",
        "options": {},
        "subject": "={{ $json.subject }}",
        "emailType": "html"
      },
      "typeVersion": 2.1
    },
    {
      "id": "c2d3e4f5-0009-4000-8000-000000000013",
      "name": "Append Report to Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1580,
        432
      ],
      "parameters": {
        "columns": {
          "value": {
            "cpa": "={{ $json.cpa }}",
            "ctr": "={{ $json.ctr }}",
            "roas": "={{ $json.roas }}",
            "spend": "={{ $json.spend }}",
            "clicks": "={{ $json.clicks }}",
            "agencyName": "={{ $json.agencyName }}",
            "clientName": "={{ $json.clientName }}",
            "highlights": "={{ $json.highlights }}",
            "next_steps": "={{ $json.next_steps }}",
            "reportDate": "={{ $json.reportDate }}",
            "conversions": "={{ $json.conversions }}",
            "impressions": "={{ $json.impressions }}",
            "watch_items": "={{ $json.watch_items }}",
            "platformName": "={{ $json.platformName }}",
            "spend_delta_pct": "={{ $json.spend_delta_pct }}",
            "executive_summary": "={{ $json.executive_summary }}",
            "reportingPeriodLabel": "={{ $json.reportingPeriodLabel }}",
            "conversions_delta_pct": "={{ $json.conversions_delta_pct }}"
          },
          "schema": [
            {
              "id": "reportDate",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reportDate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "agencyName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "agencyName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "clientName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "clientName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "platformName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "platformName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reportingPeriodLabel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reportingPeriodLabel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "spend",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "spend",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "impressions",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "impressions",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "clicks",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "clicks",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ctr",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "ctr",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "conversions",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "conversions",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cpa",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "cpa",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "roas",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "roas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "spend_delta_pct",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "spend_delta_pct",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "conversions_delta_pct",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "conversions_delta_pct",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "executive_summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "executive_summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "highlights",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "highlights",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "watch_items",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "watch_items",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_steps",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "next_steps",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Client Reports"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "REPLACE_WITH_YOUR_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Normalize Metrics": {
      "main": [
        [
          {
            "node": "Generate Client Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Branded Report": {
      "main": [
        [
          {
            "node": "Send Branded Report Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append Report to Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Monthly Schedule": {
      "main": [
        [
          {
            "node": "Set Branding and Report Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Platform Metrics": {
      "main": [
        [
          {
            "node": "Normalize Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Client Summary": {
      "main": [
        [
          {
            "node": "Build Branded Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Branding and Report Config": {
      "main": [
        [
          {
            "node": "Fetch Platform Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}