This workflow corresponds to n8n.io template #17468 — we link there as the canonical source.
This workflow follows the Form Trigger → Google Sheets 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 →
{
"id": "uqVvlBxWicEI55aK",
"name": "Audit ad campaign structures before launch with Spendict, with AI auto-restructure",
"tags": [],
"nodes": [
{
"id": "26d7e621-1b41-4eb6-a0c2-0a6345df39f2",
"name": "Campaign build brief",
"type": "n8n-nodes-base.formTrigger",
"position": [
576,
272
],
"parameters": {
"options": {},
"formTitle": "Audit a campaign build",
"formFields": {
"values": [
{
"fieldType": "dropdown",
"fieldLabel": "platform",
"fieldOptions": {
"values": [
{
"option": "meta"
},
{
"option": "google"
},
{
"option": "tiktok"
}
]
},
"requiredField": true
},
{
"fieldLabel": "objective",
"placeholder": "e.g. purchases, leads, app installs",
"requiredField": true
},
{
"fieldType": "textarea",
"fieldLabel": "campaign_structure",
"placeholder": "e.g. 1 campaign (CBO $100/day) \u2192 3 ad sets: broad, lookalike 1%, retargeting 30d \u2192 2 ads each\u2026",
"requiredField": true
},
{
"fieldLabel": "total_budget",
"placeholder": "e.g. $3,000/month (optional)"
},
{
"fieldLabel": "funnel_stage",
"placeholder": "prospecting / retargeting / retention (optional)"
}
]
},
"formDescription": "Describe the campaign structure you're about to build. Spendict audits it against the platform's rulebook before any budget moves."
},
"typeVersion": 2.2
},
{
"id": "27aafd8b-c418-4265-afc1-903a692a8421",
"name": "Spendict: audit_campaign_structure",
"type": "n8n-nodes-base.httpRequest",
"position": [
816,
272
],
"parameters": {
"url": "https://www.spendict.com/api/v1/audit-campaign",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ platform: $json.platform, objective: $json.objective, campaign_structure: $json.campaign_structure, total_budget: $json.total_budget || undefined, funnel_stage: $json.funnel_stage || undefined }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.2
},
{
"id": "a23e4345-9a21-44a0-bf1c-0ed29d1bce15",
"name": "Route on verdict",
"type": "n8n-nodes-base.switch",
"position": [
1040,
272
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "sound",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.launch_recommendation }}",
"rightValue": "sound"
}
]
},
"renameOutput": true
},
{
"outputKey": "fix_first",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.launch_recommendation }}",
"rightValue": "fix_first"
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "restructure"
}
},
"typeVersion": 3
},
{
"id": "6afd1dc6-fb5a-4eba-8663-6a2426b2d798",
"name": "Row: sound",
"type": "n8n-nodes-base.code",
"position": [
1296,
272
],
"parameters": {
"jsCode": "const brief = $('Campaign build brief').first().json;\nreturn [{ json: {\n status: 'sound as submitted',\n platform: brief.platform,\n objective: brief.objective,\n verdict: $json.launch_recommendation,\n score: $json.overall_score,\n structure: brief.campaign_structure,\n top_fix: ''\n} }];"
},
"typeVersion": 2
},
{
"id": "b44f295e-d05f-40a1-b74e-3a3a66d970da",
"name": "OpenAI: restructure",
"type": "n8n-nodes-base.httpRequest",
"position": [
1296,
672
],
"parameters": {
"url": "https://api.openai.com/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ model: 'gpt-4o-mini', response_format: { type: 'json_object' }, messages: [ { role: 'system', content: 'You are a senior media buyer. Rewrite the campaign structure to resolve every listed problem while keeping the objective and budget realistic. Describe the revised build in the same free-text style. Return ONLY JSON.' }, { role: 'user', content: 'Platform: ' + $('Campaign build brief').first().json.platform + '. Objective: ' + $('Campaign build brief').first().json.objective + '. Original structure: ' + $('Campaign build brief').first().json.campaign_structure + '. Problems to fix: ' + JSON.stringify($json.prioritized_fixes) + '. Return {\"campaign_structure\":\"...\"}' } ] }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.2
},
{
"id": "2929d9dc-9cea-489e-9fac-bc307f78ce9e",
"name": "Parse revised structure",
"type": "n8n-nodes-base.code",
"position": [
1536,
672
],
"parameters": {
"jsCode": "const brief = $('Campaign build brief').first().json;\nlet revised = '';\ntry {\n revised = JSON.parse($json.choices[0].message.content).campaign_structure || '';\n} catch (e) {\n revised = '';\n}\nif (!revised) {\n throw new Error('Could not parse the restructured build from OpenAI');\n}\nreturn [{ json: {\n platform: brief.platform,\n objective: brief.objective,\n campaign_structure: revised,\n total_budget: brief.total_budget || '',\n funnel_stage: brief.funnel_stage || ''\n} }];"
},
"typeVersion": 2
},
{
"id": "04e6a91c-2e2c-46a7-a588-99881a3fdccb",
"name": "Spendict: re-audit",
"type": "n8n-nodes-base.httpRequest",
"position": [
1760,
672
],
"parameters": {
"url": "https://www.spendict.com/api/v1/audit-campaign",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ platform: $json.platform, objective: $json.objective, campaign_structure: $json.campaign_structure, total_budget: $json.total_budget || undefined, funnel_stage: $json.funnel_stage || undefined }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.2
},
{
"id": "4ae3a215-82ec-48d2-92d2-f32022178dfa",
"name": "Row: restructured",
"type": "n8n-nodes-base.code",
"position": [
2000,
672
],
"parameters": {
"jsCode": "const revised = $('Parse revised structure').item.json;\nconst appliedFixes = $('Spendict: audit_campaign_structure').first().json.prioritized_fixes || [];\nreturn [{ json: {\n status: 'auto-restructured',\n platform: revised.platform,\n objective: revised.objective,\n verdict: $json.launch_recommendation,\n score: $json.overall_score,\n structure: revised.campaign_structure,\n top_fix: appliedFixes.length ? (appliedFixes[0].suggestion || appliedFixes[0].issue || '') : ''\n} }];"
},
"typeVersion": 2
},
{
"id": "3c5b9a19-b40c-475d-95af-d0b517f83e6d",
"name": "Collect results",
"type": "n8n-nodes-base.merge",
"position": [
2016,
272
],
"parameters": {},
"typeVersion": 3
},
{
"id": "57991d2d-0ddf-4050-9e09-85ad35086a2b",
"name": "Google Sheets: audit log",
"type": "n8n-nodes-base.googleSheets",
"position": [
2256,
272
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "47f9d558-8689-46f5-b55f-ba537d64509b",
"name": "Slack: audit result",
"type": "n8n-nodes-base.httpRequest",
"position": [
2480,
272
],
"parameters": {
"url": "https://hooks.slack.com/services/REPLACE_WITH_YOUR_SLACK_WEBHOOK",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ text: '\ud83e\uddf1 Structure audit: ' + $json.status + ' \u2014 verdict ' + $json.verdict + ' (' + $json.score + '/100)' + ($json.top_fix ? '. Top fix applied: ' + $json.top_fix : '') }) }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "19e53719-9232-4c06-a476-ee0a6ee9cd6b",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
-32
],
"parameters": {
"width": 480,
"height": 960,
"content": "## Audit campaign structure before launch\n\nPaste a campaign build into a form and Spendict audits it against the platform's rulebook before any budget moves \u2014 then has OpenAI rebuild the ones that fail and re-audits the result.\n\n### How it works\n\n1. A form takes the campaign build as free text: structure, budgets, audiences and bid strategy.\n2. Spendict's `audit_campaign_structure` judges it against the correct platform's rules and returns a verdict with prioritized fixes.\n3. A Switch routes on that verdict. A `sound` build passes straight to the log, ready to build as submitted.\n4. `fix_first` and `restructure` builds go to OpenAI along with Spendict's fixes; it rewrites the structure and Spendict re-audits the revision.\n5. Every audited build \u2014 original or rebuilt \u2014 is appended to Google Sheets and the verdict is posted to Slack.\n\n### Setup\n\n- **Spendict** \u2014 free key at spendict.com/dashboard, then Header Auth `Authorization: Bearer spd_live_\u2026` on both Spendict nodes.\n- **OpenAI** \u2014 Header Auth `Authorization: Bearer sk-\u2026` on the OpenAI node.\n- **Google Sheets** \u2014 connect Google and pick a sheet with headers `platform`, `objective`, `verdict`, `score`, `primary_leak`, `structure`.\n- **Slack** \u2014 paste your Incoming Webhook URL into the Slack node.\n\n### Customization tips\n\nPoint the form at your own build template, or swap it for a webhook fed by your campaign planner."
},
"typeVersion": 1
},
{
"id": "7b42c7ba-4c26-4a1c-9550-83d744489931",
"name": "Section: submit and audit",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
-32
],
"parameters": {
"color": 7,
"width": 1140,
"height": 524,
"content": "## Submit and audit build\n\nA form takes the build as free text. Spendict audits it against the platform rulebook and routes on the verdict; sound builds pass straight to the log."
},
"typeVersion": 1
},
{
"id": "83c6e1bb-a6f2-4052-8862-a1ebc8e813ed",
"name": "Section: auto-restructure",
"type": "n8n-nodes-base.stickyNote",
"position": [
1232,
640
],
"parameters": {
"color": 7,
"width": 940,
"height": 412,
"content": "\n\n\n\n\n\n\n\n\n\n\n## Auto-restructure the failures\n\nFailing builds go to OpenAI with Spendict's fixes, get rewritten, and are re-audited before they are accepted."
},
"typeVersion": 1
},
{
"id": "7ebb2379-7c69-42f9-a8c2-1f608c444ddc",
"name": "Section: log and notify",
"type": "n8n-nodes-base.stickyNote",
"position": [
1936,
-32
],
"parameters": {
"color": 7,
"width": 732,
"height": 524,
"content": "## Log and notify\n\nEvery audited build \u2014 sound or rebuilt \u2014 is appended to Google Sheets and the verdict is posted to Slack."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "cf2977f0-fab5-4768-a89c-518ec54e7577",
"nodeGroups": [],
"connections": {
"Row: sound": {
"main": [
[
{
"node": "Collect results",
"type": "main",
"index": 0
}
]
]
},
"Collect results": {
"main": [
[
{
"node": "Google Sheets: audit log",
"type": "main",
"index": 0
}
]
]
},
"Route on verdict": {
"main": [
[
{
"node": "Row: sound",
"type": "main",
"index": 0
}
],
[
{
"node": "OpenAI: restructure",
"type": "main",
"index": 0
}
],
[
{
"node": "OpenAI: restructure",
"type": "main",
"index": 0
}
]
]
},
"Row: restructured": {
"main": [
[
{
"node": "Collect results",
"type": "main",
"index": 1
}
]
]
},
"Spendict: re-audit": {
"main": [
[
{
"node": "Row: restructured",
"type": "main",
"index": 0
}
]
]
},
"OpenAI: restructure": {
"main": [
[
{
"node": "Parse revised structure",
"type": "main",
"index": 0
}
]
]
},
"Campaign build brief": {
"main": [
[
{
"node": "Spendict: audit_campaign_structure",
"type": "main",
"index": 0
}
]
]
},
"Parse revised structure": {
"main": [
[
{
"node": "Spendict: re-audit",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets: audit log": {
"main": [
[
{
"node": "Slack: audit result",
"type": "main",
"index": 0
}
]
]
},
"Spendict: audit_campaign_structure": {
"main": [
[
{
"node": "Route on verdict",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow collects an ad campaign build brief via an n8n form, audits the structure with Spendict, and automatically rewrites failing builds with OpenAI before re-auditing, then logs the outcome to Google Sheets and posts the verdict to Slack. Receives a campaign build brief…
Source: https://n8n.io/workflows/17468/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow collects a product brief via an n8n form, uses OpenAI to generate ad variants, gates each one with Spendict run/fix/kill scoring, auto-rewrites fixable ads, logs approved results to Goog
This workflow collects a campaign brief via an n8n form, uses Spendict to generate a targeting strategy, uses OpenAI to draft platform-specific ad variants, then has Spendict score and gate each draft
Generate realistic, high-quality images from text prompts using the Flux AI Text-to-Image Generator API via RapidAPI, and seamlessly store the results in Google Drive and log them in Google Sheets — a
This workflow hosts an n8n intake form, sends submissions to Anthropic Claude for practice-area classification and qualification, then routes results to Gmail and Google Sheets before returning a conf
This workflow collects SIP and mutual fund details via an n8n Form, calculates goal and performance metrics, generates a personalized advisory using OpenAI, emails a color-coded HTML report via Gmail,