{
  "id": "rYAWl7zx5rK3nUzt",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Generate Facebook ad sets and ads from Google Sheets rows",
  "tags": [],
  "nodes": [
    {
      "id": "b8294a2c-1f40-4b3c-8d20-2d1f41646924",
      "name": "Watch Ad Rows in Google Sheets",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        16,
        432
      ],
      "parameters": {
        "event": "rowUpdate",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyX",
              "unit": "minutes",
              "value": 5
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Facebook Ads Queue"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "898faa4e-add2-4536-b681-32252030b9f5",
      "name": "Check Row Marked For Generation",
      "type": "n8n-nodes-base.if",
      "position": [
        320,
        432
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Watch Ad Rows in Google Sheets').item.json['Generate Ad'] }}",
              "rightValue": "generate"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c4c5588d-1859-43fa-ae32-762ce80b488a",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        752,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "hooks",
              "name": "hooks",
              "type": "string",
              "value": "={{ $('Watch Ad Rows in Google Sheets').item.json['Hooks'] }}"
            },
            {
              "id": "render_url",
              "name": "render_url",
              "type": "string",
              "value": "={{ $('Watch Ad Rows in Google Sheets').item.json['Render URL'] }}"
            },
            {
              "id": "row_number",
              "name": "row_number",
              "type": "number",
              "value": "={{ $('Watch Ad Rows in Google Sheets').item.json.row_number }}"
            },
            {
              "id": "facebook_ad_account_id",
              "name": "facebook_ad_account_id",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Your Facebook Ad Account ID, including the act_ prefix (e.g. act_1234567890)__>"
            },
            {
              "id": "facebook_page_id",
              "name": "facebook_page_id",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Your Facebook Page ID__>"
            },
            {
              "id": "campaign_id",
              "name": "campaign_id",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Existing Facebook campaign ID this ad set will belong to__>"
            },
            {
              "id": "pixel_id",
              "name": "pixel_id",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Optional: Meta Pixel ID for conversion tracking (leave blank if unused)__>"
            },
            {
              "id": "custom_event_type",
              "name": "custom_event_type",
              "type": "string",
              "value": "PURCHASE"
            },
            {
              "id": "graph_api_version",
              "name": "graph_api_version",
              "type": "string",
              "value": "v21.0"
            },
            {
              "id": "daily_budget_cents",
              "name": "daily_budget_cents",
              "type": "number",
              "value": 1000
            },
            {
              "id": "age_min",
              "name": "age_min",
              "type": "number",
              "value": 18
            },
            {
              "id": "age_max",
              "name": "age_max",
              "type": "number",
              "value": 65
            },
            {
              "id": "target_genders",
              "name": "target_genders",
              "type": "array",
              "value": "={{ [1, 2] }}"
            },
            {
              "id": "target_countries",
              "name": "target_countries",
              "type": "array",
              "value": "={{ [\"US\"] }}"
            },
            {
              "id": "optimization_goal",
              "name": "optimization_goal",
              "type": "string",
              "value": "LINK_CLICKS"
            },
            {
              "id": "billing_event",
              "name": "billing_event",
              "type": "string",
              "value": "IMPRESSIONS"
            },
            {
              "id": "bid_strategy",
              "name": "bid_strategy",
              "type": "string",
              "value": "LOWEST_COST_WITHOUT_CAP"
            },
            {
              "id": "destination_url",
              "name": "destination_url",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__https://yourwebsite.com/landing-page__>"
            },
            {
              "id": "cta_type",
              "name": "cta_type",
              "type": "string",
              "value": "LEARN_MORE"
            },
            {
              "id": "ad_set_name_prefix",
              "name": "ad_set_name_prefix",
              "type": "string",
              "value": "AI Generated Ad Set"
            },
            {
              "id": "ad_status",
              "name": "ad_status",
              "type": "string",
              "value": "PAUSED"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0e058c90-1b04-471c-bc08-77a35168c26c",
      "name": "Create Facebook Ad Set",
      "type": "n8n-nodes-base.facebookGraphApi",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        944,
        352
      ],
      "parameters": {
        "edge": "adsets",
        "node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
        "options": {
          "queryParametersJson": "={{\n  // Build the ad set payload from the Configuration node.\n  (() => {\n    const cfg = $(\"Configuration\").item.json;\n    const targeting = {\n      age_min: cfg.age_min,\n      age_max: cfg.age_max,\n      genders: cfg.target_genders,\n      geo_locations: { countries: cfg.target_countries }\n    };\n    const payload = {\n      name: cfg.ad_set_name_prefix + \" - \" + String(cfg.hooks).slice(0, 40),\n      campaign_id: cfg.campaign_id,\n      daily_budget: cfg.daily_budget_cents,\n      billing_event: cfg.billing_event,\n      optimization_goal: cfg.optimization_goal,\n      bid_strategy: cfg.bid_strategy,\n      targeting: JSON.stringify(targeting),\n      status: cfg.ad_status\n    };\n    if (cfg.pixel_id) {\n      payload.promoted_object = JSON.stringify({ pixel_id: cfg.pixel_id, custom_event_type: cfg.custom_event_type });\n    }\n    return payload;\n  })()\n}}"
        },
        "graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
        "httpRequestMethod": "POST"
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 5000
    },
    {
      "id": "824f92a9-4e62-4a53-899d-b3ac4c8e9f4f",
      "name": "Upload Ad Image to Facebook",
      "type": "n8n-nodes-base.facebookGraphApi",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1184,
        336
      ],
      "parameters": {
        "edge": "adimages",
        "node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
        "options": {
          "queryParameters": {
            "parameter": [
              {
                "name": "url",
                "value": "={{ $('Configuration').item.json.render_url }}"
              }
            ]
          }
        },
        "graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
        "httpRequestMethod": "POST"
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 5000
    },
    {
      "id": "acef61f2-5ff9-44c7-9945-da3fb8113c55",
      "name": "Create Facebook Ad Creative",
      "type": "n8n-nodes-base.facebookGraphApi",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1472,
        320
      ],
      "parameters": {
        "edge": "adcreatives",
        "node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
        "options": {
          "queryParametersJson": "={{\n  // Build the ad creative payload, pulling the uploaded image hash.\n  (() => {\n    const cfg = $(\"Configuration\").item.json;\n    const imagesObj = $(\"Upload Ad Image to Facebook\").item.json.images || {};\n    const firstImage = Object.values(imagesObj)[0];\n    const imageHash = firstImage ? firstImage.hash : \"\";\n    const story = {\n      page_id: cfg.facebook_page_id,\n      link_data: {\n        message: cfg.hooks,\n        link: cfg.destination_url,\n        image_hash: imageHash,\n        call_to_action: { type: cfg.cta_type, value: { link: cfg.destination_url } }\n      }\n    };\n    return {\n      name: \"AI Creative - \" + String(cfg.hooks).slice(0, 40),\n      object_story_spec: JSON.stringify(story)\n    };\n  })()\n}}"
        },
        "graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
        "httpRequestMethod": "POST"
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 5000
    },
    {
      "id": "9156c0c2-570b-4143-9f8a-2f8134d37143",
      "name": "Create Facebook Ad",
      "type": "n8n-nodes-base.facebookGraphApi",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1872,
        304
      ],
      "parameters": {
        "edge": "ads",
        "node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
        "options": {
          "queryParametersJson": "={{\n  (() => {\n    const cfg = $(\"Configuration\").item.json;\n    return {\n      name: \"AI Ad - \" + String(cfg.hooks).slice(0, 40),\n      adset_id: $(\"Create Facebook Ad Set\").item.json.id,\n      creative: JSON.stringify({ creative_id: $(\"Create Facebook Ad Creative\").item.json.id }),\n      status: cfg.ad_status\n    };\n  })()\n}}"
        },
        "graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
        "httpRequestMethod": "POST"
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 5000
    },
    {
      "id": "80ce5e54-06ac-44e6-8724-bb20f218fefc",
      "name": "Update Sheet - Ad Generated",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2176,
        368
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Facebook Ads Queue"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "9ea2f084-9d80-41e5-b168-8840089b7881",
      "name": "Combine Ad Creation Errors",
      "type": "n8n-nodes-base.merge",
      "position": [
        1536,
        736
      ],
      "parameters": {
        "numberInputs": 4
      },
      "typeVersion": 3.2
    },
    {
      "id": "ccdeffd2-e7d3-46d2-bf28-d0898d5919d7",
      "name": "Skip - Not Marked For Generation",
      "type": "n8n-nodes-base.noOp",
      "position": [
        560,
        448
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "01eaacd4-4fb0-4164-9df4-408f2d5f3156",
      "name": "Update Sheet - Generation Error",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1776,
        464
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Facebook Ads Queue"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4d72f201-1ab7-4da5-9d03-817305a38f1a",
      "name": "Overview & Documentation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -608,
        -144
      ],
      "parameters": {
        "color": 4,
        "width": 460,
        "height": 1572,
        "content": "# Generate Facebook ad sets and ads from Google Sheets rows\n# \ud83d\udce5 [Open full documentation on Notion](https://automatisation.notion.site/Course-Generate-Facebook-ad-sets-and-ads-from-Google-Sheets-rows-3a03d6550fd981ca954febe5dd9e7c49)\n\n## How it works\nUpdate a row in a Google Sheet (Hooks + Render URL + set \"Generate Ad\" to generate) and this workflow automatically creates a Facebook ad set, uploads the creative image, builds an ad creative, and launches a paused ad - then writes the result back into the sheet.\n\n## Setup\n1. Copy the Google Sheet template (columns: Hooks, Render URL, Generate Ad, Ad ID, Error Message).\n2. Connect your Google Sheets credential and pick your sheet on the trigger and both Update nodes.\n3. Create a Facebook Graph API credential (long-lived access token) and select it on the 4 Facebook nodes.\n4. Open the Configuration node and fill in your Ad Account ID, Page ID, Campaign ID and targeting defaults.\n\n## Requirements\n- A configured Facebook Business account, App, Page and Ad Account\n- A Facebook Graph API access token with ads_management permission\n- Real advertising costs apply once ads are unpaused in Ads Manager\n\n## Customization\n- Add interest-based targeting via a flexible_spec array in the Ad Set payload\n- Swap the single-image creative for a carousel or video creative\n- Wire a Telegram or Gmail node on the error branch for real-time alerts\n\nNeed help customizing?\nContact me for consulting and support : [Linkedin](https://www.linkedin.com/in/doctor-firass/)\n\n# MY NEW YOUTUBE CHANNEL\n\ud83d\udc49 [Subscribe to my new YouTube channel](https://www.youtube.com/@DrFiras_AI). Here I'll share videos and Shorts with practical tutorials and FREE templates for n8n.\n\n[![The AI Doctor](https://www.dr-firas.com/the-ai-doctor.png)](https://www.youtube.com/@DrFiras_AI)"
      },
      "typeVersion": 1
    },
    {
      "id": "b39d4f19-0da5-4cf3-b11e-361f08fff8d5",
      "name": "Sticky Note 4fccdb14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 556,
        "height": 380,
        "content": "### 1. Watch for rows ready to generate\nPolls the sheet and only continues when \"Generate Ad\" is set to generate."
      },
      "typeVersion": 1
    },
    {
      "id": "df466c2d-38c5-478d-9d16-97125ac188e1",
      "name": "Sticky Note fe06719a",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 232,
        "content": "### 2. Configuration\nEvery adjustable value lives here: Ad Account, Page, Campaign, targeting defaults, budget, CTA and ad status (defaults to PAUSED for safety)."
      },
      "typeVersion": 1
    },
    {
      "id": "b0e4bfc5-1dea-4842-8af6-7ee3470e20f3",
      "name": "Sticky Note 48b8f0e2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 1044,
        "height": 200,
        "content": "### 3. Build & launch the ad\nCreates the ad set, uploads the image (Facebook fetches it directly from the Render URL), builds the creative, then creates the ad."
      },
      "typeVersion": 1
    },
    {
      "id": "6cb0c3cd-6470-4faa-9033-acf5509aa7f2",
      "name": "Sticky Note 8c358af9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2048,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 476,
        "height": 480,
        "content": "### 4. Write the result back\nSuccess writes the Ad ID and marks the row generated; any failure marks the row error with the message."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "d0b8653f-99dc-4a49-9071-0137f34d9b3a",
  "nodeGroups": [],
  "connections": {
    "Configuration": {
      "main": [
        [
          {
            "node": "Create Facebook Ad Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Facebook Ad": {
      "main": [
        [
          {
            "node": "Update Sheet - Ad Generated",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Combine Ad Creation Errors",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Create Facebook Ad Set": {
      "main": [
        [
          {
            "node": "Upload Ad Image to Facebook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Combine Ad Creation Errors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Ad Creation Errors": {
      "main": [
        [
          {
            "node": "Update Sheet - Generation Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Facebook Ad Creative": {
      "main": [
        [
          {
            "node": "Create Facebook Ad",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Combine Ad Creation Errors",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Upload Ad Image to Facebook": {
      "main": [
        [
          {
            "node": "Create Facebook Ad Creative",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Combine Ad Creation Errors",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Watch Ad Rows in Google Sheets": {
      "main": [
        [
          {
            "node": "Check Row Marked For Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Row Marked For Generation": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Skip - Not Marked For Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}