AutomationFlowsAI & RAG › Create and Validate Meta Ad Copy with Gpt-4o, Originalvoices, and Sheets

Create and Validate Meta Ad Copy with Gpt-4o, Originalvoices, and Sheets

ByVedad Sose @vedad27 on n8n.io

Generate 50 Meta ad copy variations informed by target audience insights, then validate with real human feedback to identify the top 10 performers — all before spending a dollar on ads.

Event trigger★★★★☆ complexityAI-powered15 nodesForm TriggerAgentOpenAI ChatMcp Client ToolGoogle Sheets
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Form Trigger 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
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Create relevant Meta ads with GPT-4o, OriginalVoices, and Google Sheets",
  "tags": [
    {
      "name": "Marketing"
    },
    {
      "name": "AI"
    },
    {
      "name": "Meta Ads"
    },
    {
      "name": "Copywriting"
    }
  ],
  "nodes": [
    {
      "id": "sticky-main-overview",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 380,
        "height": 780,
        "content": "## Create relevant Meta ads with GPT-4o, OriginalVoices, and Google Sheets\nGenerate 50 ad variations **informed by audience insights**, then **validate with real human feedback** to identify the top 10.\n\n### How it works\n1. **Form** collects product details and target audience\n2. **AI gathers insights** from Digital Twins to understand your audience\n3. **Generate 50 variations** shaped by real human preferences\n4. **Digital Twins validate** each ad for resonance\n5. **Top 10 ranked** to Google Sheet with scores and feedback\n\n### Setup\n1. Connect **OpenAI API** credentials on Chat Model nodes\n2. Connect **Header Auth** on OriginalVoices node:\n   - Header: `X-Api-Key`\n   - Value: `YOUR_API_KEY`\n3. Connect **Google Sheets OAuth** credentials\n4. Create a Google Sheet with tab \"Results\"\n5. **Activate** the workflow\n\n### Customize\n- **Variation count**: Adjust agent prompt\n- **Validation criteria**: Modify Digital Twin questions\n- **Output format**: Add columns as needed\n\n### Need help?\nReach out on Discord (`vedad27`) or email `vedad@originalvoices.ai`"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-trigger",
      "name": "Sticky Note Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 272,
        "content": "### 1. Input Form"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-generate",
      "name": "Sticky Note Generate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 468,
        "content": "### 2. Generate 50 Variations\nConnect OpenAI credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-validate",
      "name": "Sticky Note Validate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 500,
        "height": 468,
        "content": "### 3. Filter & Validate\nConnect OriginalVoices credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-output",
      "name": "Sticky Note Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1792,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 292,
        "content": "### 4. Output to Sheet\nConnect Google Sheets credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "form-trigger",
      "name": "Ad Brief Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        496,
        208
      ],
      "parameters": {
        "options": {},
        "formTitle": "Meta Ad Copy Generator",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Product/Brand Name",
              "placeholder": "e.g., Acme Fitness App",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Product Description",
              "placeholder": "What does your product do? Key features?",
              "requiredField": true
            },
            {
              "fieldLabel": "Target Audience",
              "placeholder": "e.g., US women, 25-40, health-conscious, busy professionals",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Key Benefits",
              "placeholder": "2-3 main value propositions (one per line)",
              "requiredField": true
            },
            {
              "fieldLabel": "Tone",
              "placeholder": "e.g., conversational, urgent, professional, playful",
              "requiredField": true
            },
            {
              "fieldLabel": "Landing Page URL",
              "placeholder": "https://... (optional, for context)"
            },
            {
              "fieldLabel": "Google Sheet URL",
              "placeholder": "https://docs.google.com/spreadsheets/d/...",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Generate and validate Meta ad copy variations using AI and real human feedback."
      },
      "typeVersion": 2.2
    },
    {
      "id": "generate-variations",
      "name": "Generate 50 Variations",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        768,
        208
      ],
      "parameters": {
        "text": "=You are an expert Meta Ads copywriter. Generate 50 unique ad copy variations for the following product.\n\nPRODUCT INFO:\n- Name: {{ $json['Product/Brand Name'] }}\n- Description: {{ $json['Product Description'] }}\n- Target Audience: {{ $json['Target Audience'] }}\n- Key Benefits: {{ $json['Key Benefits'] }}\n- Tone: {{ $json['Tone'] }}\n{{ $json['Landing Page URL'] ? '- Landing Page: ' + $json['Landing Page URL'] : '' }}\n\nGENERATE 50 VARIATIONS using these angles (mix them up):\n1. **Benefit-focused** (10 variations) - Lead with the transformation/outcome\n2. **Problem-aware** (10 variations) - Acknowledge the pain point, offer solution\n3. **Social proof** (8 variations) - Imply popularity, results others have seen\n4. **Urgency/Scarcity** (7 variations) - Time-sensitive or limited availability\n5. **Curiosity/Question** (8 variations) - Open loops, make them want to know more\n6. **Direct/Bold** (7 variations) - Straightforward value proposition\n\nFor EACH variation provide:\n- primary_text: The main ad copy (2-4 sentences, first 125 chars are crucial as hook)\n- headline: Bold text below creative (under 40 chars, punchy)\n\nOutput ONLY a valid JSON array with 50 objects. No markdown, no explanation.\n\nExample format:\n[\n  {\"id\": 1, \"angle\": \"benefit\", \"primary_text\": \"...\", \"headline\": \"...\"},\n  {\"id\": 2, \"angle\": \"problem\", \"primary_text\": \"...\", \"headline\": \"...\"}\n]",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "openai-model",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        768,
        432
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o"
        },
        "options": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "parse-variations",
      "name": "Parse Variations",
      "type": "n8n-nodes-base.code",
      "position": [
        1056,
        208
      ],
      "parameters": {
        "jsCode": "const output = $input.first().json.output;\n// Strip markdown code blocks if present\nlet jsonStr = output;\nif (typeof output === 'string') {\n  jsonStr = output.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n}\nconst variations = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr;\n\n// Pass along form data for later use\nconst formData = $('Ad Brief Form').first().json;\n\nreturn [{ \n  json: { \n    variations,\n    formData,\n    variationCount: variations.length\n  } \n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "validate-with-twins",
      "name": "Filter & Validate with Digital Twins",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1296,
        208
      ],
      "parameters": {
        "text": "=You are evaluating Meta ad copy variations with real human feedback.\n\nTARGET AUDIENCE: {{ $json.formData['Target Audience'] }}\n\nYou have access to the ask_twins tool which queries Digital Twins (AI representations of real people matching your target demographic).\n\nHere are 50 ad variations to evaluate:\n{{ JSON.stringify($json.variations, null, 2) }}\n\n---\n\nTASK: Use Digital Twins to identify the TOP 10 most compelling variations.\n\nSTEP 1: INITIAL FILTER\nUse ask_twins to find scroll-stoppers. \nCRITICAL: Digital Twins CANNOT see the variations - you MUST embed the full ad copy in your question.\n\nCall ask_twins with:\n- audience: \"{{ $json.formData['Target Audience'] }}\"\n- questions: Create 2-3 questions that embed batches of variations (e.g., embed 15-20 per question)\n  - Ask: \"Which of these ads would make you stop scrolling? [embed ads]\"\n  - Ask: \"Which headlines grab your attention? [embed headlines]\"\n\nSTEP 2: DEEP VALIDATION ON TOP 10\nFor the top 10 identified, use ask_twins again:\n- Embed the full primary_text + headline for each\n- Ask about: clarity, believability, purchase intent\n- Get specific feedback on what works and what doesn't\n\n---\n\nOUTPUT FORMAT:\nAfter both steps, output ONLY a JSON array of the top 10 ranked by resonance.\nNo markdown, no explanation - just the JSON.\n\n[\n  {\n    \"rank\": 1,\n    \"id\": <original id>,\n    \"primary_text\": \"...\",\n    \"headline\": \"...\",\n    \"angle\": \"...\",\n    \"resonance_score\": <1-10>,\n    \"feedback\": \"Why this resonated with the target audience\"\n  }\n]",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "openai-model-twins",
      "name": "OpenAI Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1296,
        432
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o"
        },
        "options": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "ov-mcp-tool",
      "name": "OriginalVoices Digital Twins",
      "type": "@n8n/n8n-nodes-langchain.mcpClientTool",
      "position": [
        1440,
        432
      ],
      "parameters": {
        "options": {},
        "endpointUrl": "https://api.originalvoices.ai/mcp",
        "authentication": "headerAuth"
      },
      "typeVersion": 1.2
    },
    {
      "id": "parse-results",
      "name": "Parse Results",
      "type": "n8n-nodes-base.code",
      "position": [
        1600,
        208
      ],
      "parameters": {
        "jsCode": "const output = $input.first().json.output;\nconst formData = $('Parse Variations').first().json.formData;\n\n// Strip markdown code blocks if present\nlet jsonStr = output;\nif (typeof output === 'string') {\n  jsonStr = output.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n}\nconst topAds = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr;\n\n// Extract sheet ID from URL\nconst sheetUrl = formData['Google Sheet URL'];\nconst sheetIdMatch = sheetUrl.match(/\\/d\\/([a-zA-Z0-9-_]+)/);\nconst sheetId = sheetIdMatch ? sheetIdMatch[1] : null;\n\nreturn [{ \n  json: { \n    topAds,\n    sheetId,\n    productName: formData['Product/Brand Name']\n  } \n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "format-for-sheets",
      "name": "Format for Sheets",
      "type": "n8n-nodes-base.code",
      "position": [
        1872,
        208
      ],
      "parameters": {
        "jsCode": "const topAds = $input.first().json.topAds;\nconst productName = $input.first().json.productName;\n\n// Convert array to individual rows for Google Sheets\nreturn topAds.map(ad => ({\n  json: {\n    'Rank': ad.rank,\n    'Primary Text': ad.primary_text,\n    'Headline': ad.headline,\n    'Angle': ad.angle,\n    'Resonance Score': ad.resonance_score,\n    'Feedback': ad.feedback,\n    'Product': productName\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "write-to-sheet",
      "name": "Write to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2080,
        208
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Results"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Parse Results').first().json.sheetId }}"
        }
      },
      "typeVersion": 4.6
    }
  ],
  "connections": {
    "Ad Brief Form": {
      "main": [
        [
          {
            "node": "Generate 50 Variations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Results": {
      "main": [
        [
          {
            "node": "Format for Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Variations": {
      "main": [
        [
          {
            "node": "Filter & Validate with Digital Twins",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for Sheets": {
      "main": [
        [
          {
            "node": "Write to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate 50 Variations",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Filter & Validate with Digital Twins",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate 50 Variations": {
      "main": [
        [
          {
            "node": "Parse Variations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OriginalVoices Digital Twins": {
      "ai_tool": [
        [
          {
            "node": "Filter & Validate with Digital Twins",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Filter & Validate with Digital Twins": {
      "main": [
        [
          {
            "node": "Parse Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

Generate 50 Meta ad copy variations informed by target audience insights, then validate with real human feedback to identify the top 10 performers — all before spending a dollar on ads.

Source: https://n8n.io/workflows/13222/ — 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

🎯 Create viral TikToks, Shorts, Reels, podcasts, and ASMR videos in minutes — all on autopilot.

OpenAI, HTTP Request, Form Trigger +7
AI & RAG

Digistars - Scrape & Crawl. Uses httpRequest, n8n-nodes-firecrawl-scraper, googleSheets, lmChatOpenAi. Event-driven trigger; 63 nodes.

HTTP Request, N8N Nodes Firecrawl Scraper, Google Sheets +5
AI & RAG

🧠 Automate end-to-end SEO blog creation and WordPress publishing using a GPT-5 multi-agent workflow with real-time research, metadata generation, and optional featured images.

Output Parser Structured, HTTP Request, OpenAI +10
AI & RAG

This template enables natural-language-driven automation using Bright Data's MCP tools, triggered directly by new leads in HubSpot. It dynamically extracts and executes the right tool based on lead co

Google Sheets Trigger, Output Parser Structured, Output Parser Autofixing +7
AI & RAG

Get a 360 Social media presence report for a person

Form Trigger, Mcp Client Tool, Agent +5