AutomationFlowsAI & RAG › Automate Weekly Meta Ad Reports with Claude Ai, Gomarble Mcp & Google Slides

Automate Weekly Meta Ad Reports with Claude Ai, Gomarble Mcp & Google Slides

BySankalp Dev @sankalpthedev on n8n.io

This automation workflow transforms Meta advertising data into executive ready presentation decks, eliminating manual report creation while ensuring stakeholders receive consistent performance insights. It generates professional Google Slides presentations from your ad campaigns…

Cron / scheduled trigger★★★★☆ complexityAI-powered15 nodesAgentMcp Client ToolGoogle DriveAnthropic ChatGmailHTTP Request
AI & RAG Trigger: Cron / scheduled Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #7695 — we link there as the canonical source.

This workflow follows the Agent → Gmail 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 →

Download .json
{
  "id": "AmaZWuAXZJlrGq5l",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Copy ofAutomate Weekly Marketing Summary Deck with Claude AI, GoMarble MCP & Google Slides",
  "tags": [],
  "nodes": [
    {
      "id": "4bf29e91-9786-4035-9cae-c795795aded5",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        580,
        -40
      ],
      "parameters": {
        "text": "={{ $json['Report Prompt'] }}",
        "options": {
          "systemMessage": "You are a senior digital marketing professional. You MUST always return exactly 5 slides in your JSON response. There should be no made up data or hallucination, but if insufficient data exists for a slide, use placeholder text like 'Data pending analysis' or 'No significant changes this period'."
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "97d9bdf1-4758-4222-b05f-4544e8e64fcc",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": ":alarm_clock: Runs every Monday at 8 AM - adjust schedule as needed",
      "position": [
        100,
        -40
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ed8dddf4-fe74-49b4-922e-a390c53a03d4",
      "name": "GoMarble MCP",
      "type": "@n8n/n8n-nodes-langchain.mcpClientTool",
      "notes": ":closed_lock_with_key: Add your GoMarble Bearer YOUR_TOKEN_HERE - get it from https://www.gomarble.ai/docs/connect-to-n8n",
      "position": [
        800,
        160
      ],
      "parameters": {
        "sseEndpoint": "https://apps.gomarble.ai/mcp-api/sse",
        "authentication": "bearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "21fa31be-5754-4917-bd6b-20862d3ea4bc",
      "name": "Download file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1780,
        -40
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{$json.presentationId}}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "5a805b50-3ae4-4488-9536-e3a569602fd4",
      "name": "Anthropic Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        520,
        160
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-20250514",
          "cachedResultName": "Claude 4 Sonnet"
        },
        "options": {}
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "381eafea-79d6-46ed-b0cb-8d0802f83e9a",
      "name": "Send Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1940,
        -40
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=Here is the Weekly Ad Performance Summary Deck - {{ $now.format('MM-DD') }}\".",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {}
            ]
          }
        },
        "subject": "=Weekly Summary Deck - {{ $now.format('MM-DD') }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "13fde168-5855-4509-8490-2dac61abd7f1",
      "name": "Report Prompt",
      "type": "n8n-nodes-base.set",
      "notes": "\ud83d\udd27 EDIT THIS NODE to change:\n\u2022 Account Name & ID\n\u2022 Email Recipients\n\u2022 Report Settings",
      "position": [
        420,
        -40
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f2864549-d51c-4aad-b1cf-9a731e86b816",
              "name": "=Report Prompt",
              "type": "string",
              "value": "=You are a **senior performance\u2011marketing analyst**.\n\nVoice: sharp, concise, insight\u2011driven (no fluff, no marketing hype).  \nGoal: craft a weekly digest that a busy CMO can scan in < 3 min and act on immediately.  \n\nAd Account: {{ $json.accountName }}.\nTime Period: last 7 days  \n\n---\n\nCALL TOOL  \nUse **GoMarble MCP** with:  \n{\n  \"period\": \"last_7_days\"\n}  \nIt returns JSON metrics for Meta Ads.\n\n---\n\nAFTER the tool result is returned, you MUST output exactly ONE valid JSON object with EXACTLY 5 slides\u2014nothing else:\n\n{\n  \"slides\": [\n    {\n      \"title\": \"Executive Snapshot\",\n      \"body\": \"<3\u2011line paragraph>  \u25b8 One\u2011sentence topline: combined spend, revenue, ROAS, and WoW % change.\\n\u25b8 One\u2011sentence highlight of the biggest win.\\n\u25b8 One\u2011sentence note on the main risk / action gap.\"\n    },\n    {\n      \"title\": \"Channel KPIs\",\n      \"tableData\": {\n        \"Spend\": \"$X,XXX\",\n        \"Impressions\": \"XXX,XXX\",\n        \"Clicks\": \"X,XXX\",\n        \"CTR\": \"X.XX%\",\n        \"CPC\": \"$X.XX\",\n        \"Conversions\": \"XX\",\n        \"CPA\": \"$XXX\",\n        \"Revenue\": \"$X,XXX\",\n        \"ROAS\": \"X.XX\",\n        \"WoW Change\": \"+/-XX%\"\n      }\n    },\n    {\n      \"title\": \"Top Campaigns\",\n      \"body\": \"<bullet list of the single best campaign per platform with why it won (1\u2011sentence each)>\"\n    },\n    {\n      \"title\": \"Under\u2011performers\",\n      \"body\": \"<bullet list of key weak campaigns with brief reason & WoW drop (1\u2011sentence each)>\"\n    },\n    {\n      \"title\": \"Action Recos\",\n      \"body\": \"<2\u20113 crisp, prioritised recommendations the team should execute next week>\"\n    }\n  ]\n}\n\nCRITICAL RULES  \n\u2013 Return ONLY JSON (no Markdown fences, no explanations).  \n\u2013 EXACTLY 5 slide objects - NO MORE, NO LESS. This is mandatory.\n\u2013 For Channel KPIs: Use \"tableData\" object with key-value pairs for metrics (no pipe separators)\n\u2013 Use \\n for line breaks and keep sections tight and scannable\n\u2013 Keep each \"body\" < 120 words; use \\n for line breaks.  \n\u2013 Do NOT include any keys other than \"slides\".\n\u2013 The JSON must be valid and parseable.\n\u2013 Count your slides before responding - there must be exactly 5."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8ca128ec-4f51-4d38-9c3e-bbcff815e765",
      "name": "Create Presentation",
      "type": "n8n-nodes-base.httpRequest",
      "notes": ":bar_chart: Creates empty presentation",
      "position": [
        1080,
        -40
      ],
      "parameters": {
        "url": "https://slides.googleapis.com/v1/presentations",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"title\": \"Weekly Ad Report \u2013 {{ $now.format('MM-DD') }}\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSlidesOAuth2Api"
      },
      "credentials": {
        "googleSlidesOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8f85ce4e-ac53-45b3-9b59-0052787abebd",
      "name": "Ad Account",
      "type": "n8n-nodes-base.set",
      "notes": "\ud83d\udd27 EDIT THIS NODE to change:\n\u2022 Account Name & ID\n\u2022 Email Recipients\n\u2022 Report Settings",
      "position": [
        260,
        -40
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "account-name",
              "name": "accountName",
              "type": "string",
              "value": "Long Surf"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ed8739ef-8efb-466b-8ca6-99548c809e66",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        -180
      ],
      "parameters": {
        "width": 220,
        "height": 100,
        "content": "Please add the name of the Facebook ad account in the node below for which you need the summary deck."
      },
      "typeVersion": 1
    },
    {
      "id": "632111c0-3c1e-42cb-b605-83f39d55bb0d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1840,
        -180
      ],
      "parameters": {
        "width": 200,
        "height": 100,
        "content": "Please add the email ID in the node below to which you want the summary deck sent."
      },
      "typeVersion": 1
    },
    {
      "id": "7319e6ac-e678-4bcb-b0ab-6648490e5cf0",
      "name": "Validate slide output",
      "type": "n8n-nodes-base.code",
      "notes": ":lock: GUARANTEES exactly 5 slides - no more, no less",
      "position": [
        900,
        -40
      ],
      "parameters": {
        "jsCode": "console.log('Raw AI output:', $json.output);\n\ntry {\n  let jsonString = $json.output;\n  \n  // Remove markdown code fences if present\n  if (jsonString.includes('```json')) {\n    console.log('Removing markdown fences');\n    jsonString = jsonString.replace(/```json\\n?/g, '').replace(/```\\n?$/g, '');\n  }\n  \n  // Remove any leading \"json\\n\" or similar\n  jsonString = jsonString.replace(/^json\\n/g, '');\n  \n  // Clean up any extra whitespace\n  jsonString = jsonString.trim();\n  \n  console.log('Cleaned JSON string:', jsonString.substring(0, 200) + '...');\n  \n  const output = JSON.parse(jsonString);\n  console.log(`Successfully parsed! Found ${output.slides.length} slides`);\n  \n  // Return the slides directly from AI (now with tableData support)\n  return [{ json: { slides: output.slides } }];\n  \n} catch (e) {\n  console.error('JSON parsing failed:', e.message);\n  console.log('Attempting manual extraction...');\n  \n  // Try to extract JSON from between { and }\n  const match = $json.output.match(/\\{[\\s\\S]*\\}/);\n  if (match) {\n    try {\n      const output = JSON.parse(match[0]);\n      console.log('Manual extraction successful!');\n      return [{ json: { slides: output.slides } }];\n    } catch (e2) {\n      console.log('Manual extraction also failed');\n    }\n  }\n  \n  // Last resort: return fallback with table structure for Channel KPIs\n  const fallbackSlides = [\n    { \n      title: \"Executive Snapshot\", \n      body: \"Weekly performance data is currently being processed. Report will be available shortly.\" \n    },\n    { \n      title: \"Channel KPIs\", \n      tableData: {\n        \"Spend\": \"Data pending\",\n        \"Impressions\": \"Data pending\",\n        \"Clicks\": \"Data pending\",\n        \"CTR\": \"Data pending\",\n        \"CPC\": \"Data pending\",\n        \"Conversions\": \"Data pending\",\n        \"CPA\": \"Data pending\",\n        \"Revenue\": \"Data pending\",\n        \"ROAS\": \"Data pending\",\n        \"WoW Change\": \"Data pending\"\n      }\n    },\n    { \n      title: \"Top Campaigns\", \n      body: \"Campaign performance analysis is in progress. Top-performing campaigns will be highlighted once data processing is complete.\" \n    },\n    { \n      title: \"Under-performers\", \n      body: \"Performance analysis not available at this time. Underperforming campaigns will be identified in the next report.\" \n    },\n    { \n      title: \"Action Recos\", \n      body: \"Strategic recommendations will be provided once performance data analysis is complete. Please check the next scheduled report.\" \n    }\n  ];\n  \n  return [{ json: { slides: fallbackSlides } }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "7a17068d-a6ae-46e5-9b37-3c5157291bf1",
      "name": "Format Slides Data",
      "type": "n8n-nodes-base.code",
      "notes": ":wrench: Prepares the batch request with slide data",
      "position": [
        1440,
        -40
      ],
      "parameters": {
        "jsCode": "// Get the data from the Combine Data node\nconst presentationId = $json.presentationId;\nconst slides = $json.slides;\n\nconst requests = [\n  // Delete the default slide first\n  {\n    deleteObject: { objectId: 'p' }\n  }\n];\n\n// Create exactly 5 slides with content\nslides.forEach((slide, index) => {\n  const slideId = `slide_${index + 1}`;\n  const titleId = `${slideId}_title`;\n  \n  // Create slide\n  requests.push({\n    createSlide: {\n      objectId: slideId,\n      slideLayoutReference: { predefinedLayout: 'TITLE_AND_BODY' },\n      placeholderIdMappings: [\n        { layoutPlaceholder: { type: 'TITLE', index: 0 }, objectId: titleId },\n        { layoutPlaceholder: { type: 'BODY', index: 0 }, objectId: `${slideId}_body` }\n      ]\n    }\n  });\n  \n  // Add title\n  requests.push({\n    insertText: { objectId: titleId, insertionIndex: 0, text: slide.title }\n  });\n  \n  // Handle Channel KPIs slide with table\n  if (slide.title === \"Channel KPIs\" && slide.tableData) {\n    const tableId = `${slideId}_table`;\n    \n    // Create table (2 columns, 10 rows for the metrics)\n    requests.push({\n      createTable: {\n  objectId: tableId,\n  elementProperties: {\n    pageObjectId: slideId,\n    size: { width: { magnitude: 350, unit: 'PT' }, height: { magnitude: 200, unit: 'PT' } },\n    transform: {\n      scaleX: 1, scaleY: 1, translateX: 185, translateY: 80, unit: 'PT'\n    }\n  },\n  rows: 10,\n  columns: 2\n}\n    });\n    \n    // Populate table with data\n    const metrics = Object.entries(slide.tableData);\n    metrics.forEach(([metric, value], rowIndex) => {\n      // Insert metric name in first column\n      requests.push({\n        insertText: {\n          objectId: tableId,\n          cellLocation: { rowIndex: rowIndex, columnIndex: 0 },\n          insertionIndex: 0,\n          text: metric\n        }\n      });\n      \n      // Insert metric value in second column\n      requests.push({\n        insertText: {\n          objectId: tableId,\n          cellLocation: { rowIndex: rowIndex, columnIndex: 1 },\n          insertionIndex: 0,\n          text: value\n        }\n      });\n    });\n    \n  } else {\n    // For all other slides, use regular text insertion\n    const bodyId = `${slideId}_body`;\n    requests.push({\n      insertText: { objectId: bodyId, insertionIndex: 0, text: slide.body }\n    });\n  }\n});\n\nreturn [{\n  json: {\n    presentationId: presentationId,\n    batchUpdateBody: { requests: requests }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cd89420c-20be-4d70-b4b3-2b7fc0a80315",
      "name": " Build Presentation",
      "type": "n8n-nodes-base.httpRequest",
      "notes": ":page_facing_up: Executes the batch update to create all slides",
      "position": [
        1600,
        -40
      ],
      "parameters": {
        "url": "=https://slides.googleapis.com/v1/presentations/{{$json.presentationId}}:batchUpdate",
        "method": "POST",
        "options": {},
        "jsonBody": "={{JSON.stringify($json.batchUpdateBody)}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSlidesOAuth2Api"
      },
      "credentials": {
        "googleSlidesOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "27755c4f-b75f-45c8-a674-21b3826082e9",
      "name": "Merge Presentation Info",
      "type": "n8n-nodes-base.set",
      "notes": ":link: Combines presentation ID with slide data",
      "position": [
        1260,
        -40
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "presentation-id",
              "name": "presentationId",
              "type": "string",
              "value": "={{$json.presentationId}}"
            },
            {
              "id": "slides-data",
              "name": "slides",
              "type": "array",
              "value": "={{$items('Validate slide output')[0].json.slides}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "6f834adc-d3d9-466e-8f9b-36668904af1f",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Validate slide output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ad Account": {
      "main": [
        [
          {
            "node": "Report Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GoMarble MCP": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Download file": {
      "main": [
        [
          {
            "node": "Send Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Report Prompt": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Ad Account",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Slides Data": {
      "main": [
        [
          {
            "node": " Build Presentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Build Presentation": {
      "main": [
        [
          {
            "node": "Download file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Presentation": {
      "main": [
        [
          {
            "node": "Merge Presentation Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Validate slide output": {
      "main": [
        [
          {
            "node": "Create Presentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Presentation Info": {
      "main": [
        [
          {
            "node": "Format Slides Data",
            "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

This automation workflow transforms Meta advertising data into executive ready presentation decks, eliminating manual report creation while ensuring stakeholders receive consistent performance insights. It generates professional Google Slides presentations from your ad campaigns…

Source: https://n8n.io/workflows/7695/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

The Multi-Model Agency Content Engine is a high-performance editorial system designed for agencies. It solves the "blank page" problem by alternating between real-world social proof and strategic expe

Google Sheets, Gmail, Google Drive +6
AI & RAG

This workflow automates tax compliance monitoring and revenue analysis for accounting teams and finance managers handling multi-source income data. It solves the critical problem of manually tracking

n8n, Agent, HTTP Request +5
AI & RAG

LinkedIn_Job_Hunt_and_Cover_Letter. Uses outputParserStructured, outputParserAutofixing, googleDrive, agent. Scheduled trigger; 85 nodes.

Output Parser Structured, Output Parser Autofixing, Google Drive +6
AI & RAG

This n8n automation workflow automates the creation, scripting, production, and posting of YouTube videos. It leverages AI (OpenAI), image generation (PIAPI), video rendering (Shotstack), and platform

Agent, OpenAI Chat, Airtable Tool +7
AI & RAG

This workflow is for beauty salons who want consistent, high‑quality social media content without writing every post manually. It also suits agencies and automation builders who manage multiple beauty

Telegram, Google Sheets Trigger, Agent +26