AI 기반 Meta 광고 주간 PDF 보고서 - Slack 또는 이메일로 발송
중급
이것은Market Research, AI Summarization분야의자동화 워크플로우로, 14개의 노드를 포함합니다.주로 Set, Code, Slack, SplitOut, Pdforge 등의 노드를 사용하며. GPT-4洞察을 사용하여 Meta 광고 주간 성과 보고서 생성并통해 Slack 발송
사전 요구사항
- •Slack Bot Token 또는 Webhook URL
- •대상 API의 인증 정보가 필요할 수 있음
- •OpenAI API Key
사용된 노드 (14)
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "OGdHKG9jhEKDrZAC",
"meta": {
"instanceId": "33bfcf6494ef5d9b0fac5fde544f7913410659bb957613631505fa8056a7625a",
"templateCredsSetupCompleted": true
},
"name": "AI-Powered Meta Ads Weekly PDF Report – Sends to your Slack or Email",
"tags": [],
"nodes": [
{
"id": "e2a34995-dd70-432e-8bbc-3432421db01d",
"name": "분할",
"type": "n8n-nodes-base.splitOut",
"position": [
-880,
40
],
"parameters": {
"options": {},
"fieldToSplitOut": "data"
},
"typeVersion": 1
},
{
"id": "a7c0b061-00a3-47a8-9ed6-3e8b840cc6a7",
"name": "HTTP 요청 - Meta Ads",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
-1100,
40
],
"parameters": {
"url": "=https://graph.facebook.com/v23.0/act_{{$json.ad_account_id}}/insights",
"options": {
"pagination": {
"pagination": {
"nextURL": "={{ $response.body.paging.next }}",
"paginationMode": "responseContainsNextURL",
"requestInterval": 500,
"completeExpression": "={{ !$response.body.paging.next }}",
"paginationCompleteWhen": "other"
}
}
},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "fields",
"value": "=campaign_id,campaign_name,adset_id,adset_name,ad_id,ad_name,reach,impressions,inline_link_clicks,inline_link_click_ctr,conversions,spend,cpc,cost_per_conversion,action_values,purchase_roas"
},
{
"name": "time_increment",
"value": "1"
},
{
"name": "date_preset",
"value": "={{ $json.date_range }}"
},
{
"name": "level",
"value": "ad"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "Z9SGcPUjCFlHCdPM",
"name": "Meta Ads Auth"
}
},
"typeVersion": 4.2,
"alwaysOutputData": true
},
{
"id": "93076858-210e-4142-b7a1-1c0f5ba0d17d",
"name": "구성",
"type": "n8n-nodes-base.set",
"position": [
-1320,
40
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9941e108-1e6c-4050-8401-9e6125a847de",
"name": "ad_account_id",
"type": "string",
"value": "982086037409625232"
},
{
"id": "a9ca0cda-adc0-40fd-8f8c-8aa7437276c5",
"name": "date_range",
"type": "string",
"value": "last_7d"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "252d5491-0ecc-4c87-bce6-fa60e2aba035",
"name": "메모",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2040,
-80
],
"parameters": {
"width": 420,
"height": 520,
"content": "## Welcome to Weekly Meta Ads Report Workflow!\n\n**This workflow has the following sequence:**\n1. Time trigger (e.g. every Monday at 8 a.m.)\n2. Retrieval of Meta Ads data from the last 7 days (or any period you want)\n3. Format it and generate AI insight of the data\n4. Transform it into a PDF report using pdforge (Using the pre-made Meta Ads Template)\n5. Sending the report as a Slack Message (You can change it to send an e-mail, whatsapp or telegram message)\n\n\n**The following accesses are required for the workflow:**\n- Meta Ads (via Facebook Graph API): [Documentation](https://docs.n8n.io/integrations/builtin/credentials/facebookgraph/)\n- pdforge account: [Create here](https://app.pdforge.com/auth/sign-up)\n- AI API access (e.g. via OpenAI, Anthropic, Google or Ollama)\n- Slack Connection: [Documentation](https://docs.n8n.io/integrations/builtin/credentials/slack)\n\n\nYou can contact me via LinkedIn, if you have any questions: https://www.linkedin.com/in/marceloamiranda"
},
"typeVersion": 1
},
{
"id": "fa9620e6-cee4-4d75-b19b-c6c25c56a555",
"name": "메모1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1380,
-80
],
"parameters": {
"color": 4,
"width": 220,
"height": 320,
"content": "**Here you'll configure:**\n- AD Account ID\n- Date range from ads"
},
"typeVersion": 1
},
{
"id": "7ad3d2d7-571f-4e84-b8d3-a14a39b93a6e",
"name": "Pdforge",
"type": "n8n-nodes-pdforge.pdforge",
"position": [
100,
40
],
"parameters": {
"options": {},
"variables": "={{ JSON.stringify($json) }}",
"templateId": "meta_ads"
},
"credentials": {
"pdforgeApi": {
"id": "fFuvCIsBkIHHd0ct",
"name": "Pdforge account"
}
},
"typeVersion": 1
},
{
"id": "3ecfcea9-531e-47b2-8074-ed304b35f5e4",
"name": "데이터 형식화",
"type": "n8n-nodes-base.code",
"position": [
-660,
40
],
"parameters": {
"jsCode": "// 1. extract raw records\nconst records = $input.all().map(item => item.json);\n\n// 2. date utilities\nfunction parseYMD(s){ return new Date(s + 'T00:00:00'); }\nfunction formatYMD(d){\n const y = d.getFullYear();\n const m = String(d.getMonth()+1).padStart(2,'0');\n const da = String(d.getDate()).padStart(2,'0');\n return `${y}-${m}-${da}`;\n}\nfunction rangeDates(start, end){\n const a = [];\n let cur = new Date(start);\n while(cur <= end){\n a.push(formatYMD(cur));\n cur.setDate(cur.getDate()+1);\n }\n return a;\n}\n\n// 3. find global start/end\nconst dates = records.flatMap(r=>[r.date_start, r.date_stop]);\nconst minDate = new Date(Math.min(...dates.map(d=>parseYMD(d))));\nconst maxDate = new Date(Math.max(...dates.map(d=>parseYMD(d))));\nconst allLabels = rangeDates(minDate, maxDate);\n\n// 4. overview totals\nconst overviewAgg = records.reduce((acc, r) => {\n const imp = Number(r.impressions || 0);\n const clk = Number(r.inline_link_clicks || 0);\n const spd = Number(r.spend || 0);\n acc.impressions += imp;\n acc.clicks += clk;\n acc.investment += spd;\n return acc;\n}, { impressions:0, clicks:0, investment:0 });\n\nconst overview = {\n investment: { value: Number(overviewAgg.investment.toFixed(2)) },\n impressions: { value: overviewAgg.impressions },\n clicks: { value: overviewAgg.clicks },\n ctr: { \n value: overviewAgg.impressions\n ? `${(overviewAgg.clicks / overviewAgg.impressions * 100).toFixed(2)}%`\n : '0.00%' \n },\n cpc: { \n value: overviewAgg.clicks\n ? Number((overviewAgg.investment / overviewAgg.clicks).toFixed(4))\n : 0 \n }\n};\n\n// 5. day_by_day\nconst dayMap = records.reduce((m, r) => {\n const d = r.date_start;\n if (!m[d]) m[d] = { impressions:0, clicks:0 };\n m[d].impressions += Number(r.impressions || 0);\n m[d].clicks += Number(r.inline_link_clicks || 0);\n return m;\n}, {});\nconst day_by_day = {\n labels: allLabels,\n impressions: allLabels.map(d => dayMap[d]?.impressions || 0),\n clicks: allLabels.map(d => dayMap[d]?.clicks || 0),\n};\n\n// 6. helper to aggregate by key (com formatação de investimento em 2 casas)\nfunction aggBy(records, keyField){\n const map = {};\n records.forEach(r => {\n const key = r[keyField] || 'N/A';\n if (!map[key]) map[key] = { \n name: key, impressions:0, clicks:0, investment:0 \n };\n map[key].impressions += Number(r.impressions || 0);\n map[key].clicks += Number(r.inline_link_clicks || 0);\n map[key].investment += Number(r.spend || 0);\n });\n return Object.values(map).map(o => ({\n name: o.name,\n impressions: o.impressions,\n clicks: o.clicks,\n investment: Number(o.investment.toFixed(2)), // <-- 2 decimais aqui\n ctr: o.impressions\n ? `${(o.clicks / o.impressions * 100).toFixed(2)}%`\n : '0.00%',\n cpc: o.clicks\n ? Number((o.investment / o.clicks).toFixed(4))\n : 0,\n }));\n}\n\n// 7. campaigns, ad-sets, ads\nconst byCampaign = aggBy(records, \"campaign_name\");\nconst byAdSet = aggBy(records, \"adset_name\");\nconst byAd = aggBy(records, \"ad_name\");\n\n// 8. top 10 cheapest CPC\nconst top10Campaigns = [...byCampaign]\n .sort((a,b)=>a.cpc - b.cpc)\n .slice(0,10)\n .map(c=>({\n campaign_name: c.name,\n investment: c.investment,\n impressions: c.impressions,\n ctr: c.ctr,\n clicks: c.clicks,\n cpc: c.cpc\n }));\nconst top10AdSets = [...byAdSet]\n .sort((a,b)=>a.cpc - b.cpc)\n .slice(0,10)\n .map(a=>({\n ad_group_name: a.name,\n investment: a.investment,\n impressions: a.impressions,\n ctr: a.ctr,\n clicks: a.clicks,\n cpc: a.cpc\n }));\nconst top10Ads = [...byAd]\n .sort((a,b)=>a.cpc - b.cpc)\n .slice(0,10)\n .map(a=>({\n ad_group_name: a.name,\n investment: a.investment,\n impressions: a.impressions,\n ctr: a.ctr,\n clicks: a.clicks,\n cpc: a.cpc\n }));\n\n// 9. comparisons ordered by investment desc\nconst adsComparison = byAd.sort((a,b)=>b.investment - a.investment);\nconst adSetsComparison = byAdSet.sort((a,b)=>b.investment - a.investment);\n\n// 10. monta o relatório final\nconst report = {\n report_date: `${formatYMD(minDate)} - ${formatYMD(maxDate)}`,\n overview,\n day_by_day,\n top_10_campaigns: top10Campaigns,\n ad_set_comparison: {\n labels: adSetsComparison.map(a=>a.name),\n investment: adSetsComparison.map(a=>Number(a.investment.toFixed(2))),\n impressions: adSetsComparison.map(a=>a.impressions),\n clicks: adSetsComparison.map(a=>a.clicks),\n },\n top_10_ad_sets: top10AdSets,\n ads_comparison: {\n labels: adsComparison.map(a=>a.name),\n investment: adsComparison.map(a=>Number(a.investment.toFixed(2))),\n impressions: adsComparison.map(a=>a.impressions),\n clicks: adsComparison.map(a=>a.clicks),\n },\n top_10_ads: top10Ads\n};\n\n// 11. retorna um único item com o JSON pronto\nreturn [{ json: report }];"
},
"typeVersion": 2
},
{
"id": "4fc7c497-470f-4f24-adb2-e0b411c706f3",
"name": "바이너리 다운로드",
"type": "n8n-nodes-base.httpRequest",
"position": [
320,
40
],
"parameters": {
"url": "={{ $json.signedUrl }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "06bf787e-7304-401e-b522-4867bf3cec3d",
"name": "Meta Ads 인사이트 생성",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-460,
40
],
"parameters": {
"text": "={{ JSON.stringify($json) }}",
"options": {
"systemMessage": "=## Persona\nYou are a data-driven Meta Ads Insights Agent: concise, analytical, and focused on turning raw campaign data into clear, actionable recommendations.\n\n## Objective\n- Ingest structured Meta Ads data (campaigns → adsets → ads). \n- Generate three tiers of performance insights: campaign, adset, ad. \n- Keep each insight 200–300 characters, in English, ending with a practical tip.\n\n## Rules\n### Formatting\n- Output **only** a JSON object with keys: `campaign_insights`, `adset_insights`, `ad_insights`. \n- Do **not** include any additional text or explanation. \n- Ensure each insight string is between **200 and 300 characters** (inclusive).\n\n### Content\n- All insights must be written in clear, idiomatic English. \n- Each insight must:\n 1. Summarize performance (e.g., spend vs. CTR/CPC comparisons). \n 2. Highlight one key strength or weakness. \n 3. Conclude with a single, concrete, actionable tip.\n\n### Constraints\n- Base insights strictly on the supplied data. \n- Do not reference hypothetical or external factors. \n- Do not break JSON syntax or embed markdown in the output.\n\n## Step by Step Instructions\n1. **Parse Input**: Read the incoming JSON payload of `campaigns` → `adsets` → `ads`. \n2. **Aggregate Metrics**: \n - For each campaign: compute total investment, average CTR (weighted by impressions), and average CPC. \n - For each adset: compute its CTR and CPC. \n - For each ad: read its individual metrics. \n3. **Identify Highlights**: \n - Campaign: find overall high or low performance signal. \n - Adset: select the best or worst by CTR or CPC. \n - Ad: select the top or bottom ad by CTR or CPC. \n4. **Compose Insights**: For each level, draft a 200–300 character string that: \n - Summarizes performance. \n - Notes one key strength or weakness. \n - Ends with “Tip: …” giving a clear improvement action. \n5. **Validate Length**: Confirm each insight is within 200–300 characters. \n6. **Return Output**: Emit exactly the JSON object described below.\n\n## Output Format\n```json\n{\n \"campaign_insights\": \"string (200–300 chars)\",\n \"adset_insights\": \"string (200–300 chars)\",\n \"ad_insights\": \"string (200–300 chars)\"\n}\n```"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2
},
{
"id": "be4f212e-78a9-4721-82e2-883e1ac02f9e",
"name": "OpenAI 채팅 모델",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-560,
300
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1",
"cachedResultName": "gpt-4.1"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "3hJLI2ahkB4Zjnwd",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "18aa7809-12f6-4ed4-9523-5b4a8efe8140",
"name": "구조화 출력 파서",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-200,
260
],
"parameters": {
"jsonSchemaExample": "{\n \"campaign_insights\": \"string (200–300 chars)\",\n \"adset_insights\": \"string (200–300 chars)\",\n \"ad_insights\": \"string (200–300 chars)\"\n}"
},
"typeVersion": 1.3
},
{
"id": "49f0bd44-ad30-482e-9c05-9027081d7c7a",
"name": "변수 생성",
"type": "n8n-nodes-base.code",
"position": [
-100,
40
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const base = $(\"Format data\").first().json\n\nreturn {\n ...base, \n insights_content: $json.output.campaign_insights,\n insights_ad_set_content:$json.output.adset_insights,\n insights_ads_content: $json.output.ad_insights,\n };\n"
},
"typeVersion": 2
},
{
"id": "d2bfb619-b65c-4371-a074-9384bdefd34b",
"name": "Slack - 파일 첨부 메시지 전송",
"type": "n8n-nodes-base.slack",
"position": [
540,
40
],
"webhookId": "aae266d5-f681-4b86-bd2a-fceb1f5c182c",
"parameters": {
"options": {
"fileName": "=Meta ADS Report - {{ $('Generate variables').first().json.report_date }}",
"channelId": "C093723CAN6",
"initialComment": "📊 *Here's your Weekly Meta Ads Report* \\n"
},
"resource": "file",
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"id": "ombwJbyognfRtMoj",
"name": "Slack account"
}
},
"typeVersion": 2.3
},
{
"id": "0bd7647f-1f8b-4478-8e76-2a3f176ad39d",
"name": "매주 월요일 오전 8시",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1560,
40
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
}
],
"active": false,
"pinData": {},
"settings": {
"timezone": "America/Sao_Paulo",
"callerPolicy": "workflowsFromSameOwner",
"executionOrder": "v1",
"executionTimeout": -1
},
"versionId": "85fcbd4c-7f9b-4aae-b04e-99a0980bd474",
"connections": {
"7ad3d2d7-571f-4e84-b8d3-a14a39b93a6e": {
"main": [
[
{
"node": "4fc7c497-470f-4f24-adb2-e0b411c706f3",
"type": "main",
"index": 0
}
]
]
},
"e2a34995-dd70-432e-8bbc-3432421db01d": {
"main": [
[
{
"node": "3ecfcea9-531e-47b2-8074-ed304b35f5e4",
"type": "main",
"index": 0
}
]
]
},
"3ecfcea9-531e-47b2-8074-ed304b35f5e4": {
"main": [
[
{
"node": "06bf787e-7304-401e-b522-4867bf3cec3d",
"type": "main",
"index": 0
}
]
]
},
"93076858-210e-4142-b7a1-1c0f5ba0d17d": {
"main": [
[
{
"node": "a7c0b061-00a3-47a8-9ed6-3e8b840cc6a7",
"type": "main",
"index": 0
}
]
]
},
"4fc7c497-470f-4f24-adb2-e0b411c706f3": {
"main": [
[
{
"node": "d2bfb619-b65c-4371-a074-9384bdefd34b",
"type": "main",
"index": 0
}
]
]
},
"be4f212e-78a9-4721-82e2-883e1ac02f9e": {
"ai_languageModel": [
[
{
"node": "06bf787e-7304-401e-b522-4867bf3cec3d",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"49f0bd44-ad30-482e-9c05-9027081d7c7a": {
"main": [
[
{
"node": "7ad3d2d7-571f-4e84-b8d3-a14a39b93a6e",
"type": "main",
"index": 0
}
]
]
},
"0bd7647f-1f8b-4478-8e76-2a3f176ad39d": {
"main": [
[
{
"node": "93076858-210e-4142-b7a1-1c0f5ba0d17d",
"type": "main",
"index": 0
}
]
]
},
"a7c0b061-00a3-47a8-9ed6-3e8b840cc6a7": {
"main": [
[
{
"node": "e2a34995-dd70-432e-8bbc-3432421db01d",
"type": "main",
"index": 0
}
]
]
},
"18aa7809-12f6-4ed4-9523-5b4a8efe8140": {
"ai_outputParser": [
[
{
"node": "06bf787e-7304-401e-b522-4867bf3cec3d",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"d2bfb619-b65c-4371-a074-9384bdefd34b": {
"main": [
[]
]
},
"06bf787e-7304-401e-b522-4867bf3cec3d": {
"main": [
[
{
"node": "49f0bd44-ad30-482e-9c05-9027081d7c7a",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 시장 조사, AI 요약
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
GPT-4 인사이트와 Slack 전송을 사용한 주간 SEO 보고서 자동화
GPT-4 인사이트 및 Slack 배달을 통한 주간 SEO 보고서 자동화
Set
Code
Merge
+
Set
Code
Merge
42 노드Marcelo Abreu
시장 조사
Twitter 모니터링 워크플로우
OpenAI, Google Sheets 및 Slack 알림을 사용한 Twitter 감정 분석 자동화
If
Set
Slack
+
If
Set
Slack
15 노드InfyOm Technologies
시장 조사
Bright Data, OpenAI 및 Redis 기반 고급 다중 소스 AI 연구
사용Bright Data、OpenAI및Redis进行高级多源AI研究
If
Set
Code
+
If
Set
Code
43 노드Daniel Shashko
시장 조사
자동화된 회의 준비
GPT-5 및 Gemini 리서치를 사용한 캘린더에서 Slack까지 Attio CRM 통해 회의 자동 준비
If
Set
Code
+
If
Set
Code
39 노드Harry Siggins
AI 요약
23 고객 생애 가치 분석
GPT-4와 Bright Data MCP를 사용하여 고객 가치 고객 분석 및 타겟팅
If
Set
Code
+
If
Set
Code
20 노드Yaron Been
시장 조사
GPT-4o, Sheets 및 Slack을 사용한 월간 Google Ads 성과 분석 자동화
GPT-4o, Sheets 및 Slack을 사용한 자동화된 월간 Google Ads 성과 분석
Code
Slack
Http Request
+
Code
Slack
Http Request
13 노드Nikan Noorafkan
시장 조사