Plantilla HP - Calendario de contenido
Este es unContent Creation, Multimodal AIflujo de automatización del dominio deautomatización que contiene 24 nodos.Utiliza principalmente nodos como Set, Code, Wait, Merge, HttpRequest. Usar GPT-4, Apify y Google Sheets para generar automáticamente el calendario de contenido de Instagram
- •Pueden requerirse credenciales de autenticación para la API de destino
- •Credenciales de API de Google Sheets
Nodos utilizados (24)
Categoría
{
"id": "78RdoKSIQITTYT8u",
"meta": {
"instanceId": "a9966e7d53853abbaaeed78ba2b9971c959f5792b2cccdff75eb461951503a7f",
"templateCredsSetupCompleted": true
},
"name": "template HP - Content Schedule",
"tags": [
{
"id": "qGxXgaGLRNM9h4LO",
"name": "template",
"createdAt": "2025-08-04T16:37:34.891Z",
"updatedAt": "2025-08-04T16:37:34.891Z"
}
],
"nodes": [
{
"id": "99e295bb-f026-4641-a96a-098461e70cf7",
"name": "Al hacer clic en 'Probar flujo de trabajo'",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1960,
40
],
"parameters": {},
"typeVersion": 1
},
{
"id": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
"name": "Agente de IA - programación de contenido",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
720,
-20
],
"parameters": {
"text": "=Write an Instagram post based on the inputs below.\n\nPillar: {{ $json.Pillar }}\nFormat: {{ $json.Format }}\nHoliday: {{ $json.Holiday }}\nSeason: {{ $json.SeasonStage }}\nBlog Title and URL (if applicable): {{ $json.Examples }}\nBlog Description (if any): {{ $json.Description }}\n\n\n🧠 Instructions:\n1. If the Holiday is not \"None\"\nWeave the occasion into the caption and visual description with elegant emotional framing and thematic imagery.\nE.g., a father and child for Father’s Day, subtle red-white-blue palette for July 4th.\nMatch tone to the holiday — nostalgic, celebratory, or heartfelt.\n\n2. If the Pillar is “Posts based on blogs”\nUse the blog title and description to inspire a visually or emotionally powerful idea.\n\nDo not summarize the blog.\n\nYou may end with a soft call to action — but do NOT use “See more in our latest journal.”\n\nInclude a CTA if the blog content or description clearly invites it.\n\n3. If the Pillar is \"testimonials”\n\nOnly begin a caption in the testimonial pillar with [add personalized sentence], not any other pillar. \n\nDo not use quotation marks unless a real quote is supplied.\n\nFocus on the tone of the testimonial (e.g., appreciation, transformation, confidence), not the exact wording.\n \n\n3. Use SeasonStage only to shape tone and atmosphere — not always as direct mention.\nLet the stage of the season influence the emotional feel, not dominate the caption.\nAvoid starting every post with “In summer…” or “As spring begins…”.\nInstead, use:\n\nEnvironmental cues (e.g., light, textures, locations, routines)\n\nEmotional themes (e.g., renewal, warmth, intimacy)\n\nTime-sensitive urgency sparingly (e.g., “final weeks for fall bookings” in mid-October)\n\n💡 You do not need to include the season name unless it adds meaningful context or urgency.\n\n4. Use a tone that is elegant, sincere, and emotionally grounded — aligned with Hartshorn Portraiture’s brand: a boutique photography studio creating framed, luxury portrait artwork for affluent families. \n\nPrioritize clarity and connection over poetic language. Avoid metaphors, clichés, or overly descriptive scenes. Write how a thoughtful human would speak. Let the subject speak for itself — your words should complement, not compete.\n\nKeep it natural, succinct (2–4 sentences), and tailored to the post’s format and intent. Vary your message — make sure it feels fresh, specific, and distinctive from other captions.\n\n5. Visual Description\nDescribe the layout, emotion, or structure of the visual clearly.\nUse variety and specificity (e.g., quote overlay on a candid moment, elegant close-up of frames, side-by-side client transformation).\n\n📦 Output Format:\nCaption\n[Your caption here — 2–4 sentences, emotionally grounded, with clarity and variety]\n\nVisual Description\n[1 sentence describing the image or structure. Be specific.]\n\nHashtags\nInclude ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\nAdd 2 additional relevant local or theme-based hashtags (e.g., #hobokenweekend, #NJbeachportraits)",
"options": {
"systemMessage": "=You are a luxury brand copywriter for Hartshorn Portraiture, a boutique photography studio in Hoboken, NJ that creates wall-ready portrait artwork for affluent families who value connection, tradition, and timeless beauty. Your voice is elegant, warm, sincere, and human — never overly poetic, sentimental, or abstract. Captions should sound like something a real person would write, with subtle luxury cues and emotional intelligence.\n\nDo not use flowery or exaggerated phrases. Avoid repeating sentence structures or relying on vague themes without a clear visual or client connection. \n\nYour job is to generate:\n\nA caption (Instagram-ready, 2–4 sentences)\n\nA visual description (1–2 clear lines describing the image or post structure)\n\n📌 Caption Guidelines\nWrite 2–4 emotionally powerful sentences\n\nUse varied tone across captions: poetic, editorial, warm, reflective, celebratory\n\nNever repeat exact sentence patterns. Avoid empty adjectives. Favor simple, well-crafted language that speaks directly to the client’s values: family, permanence, beauty, and a home filled with meaningful art.\nUse no more than 1 subtle seasonal detail or metaphor per caption, and only if it adds meaning\n\nDo not use quote marks (\"...\") unless an actual quote is provided.\n\n🔁 Content Variation\n\nDon’t default to “family” as the emotional core in every caption. Instead, rotate across:\n\n✨ Artistry & Craftsmanship – Mac’s eye, framing, archival prints, quality\n\n🏡 Interior Impact – how artwork transforms space\n\n🌱 Milestones – maternity, personal portraits, growth\n\n💬 Client Testimonials – If the Pillar is testimonial-based:\n- Begin the caption with this exact placeholder (do not replace it): [add personalized sentence]\n- Do not use quotation marks unless quoting a real client\n- Reflect the tone of the testimonial (e.g., appreciation, confidence, transformation), not the literal language\n- Focus on the client’s emotional journey or experience, not specific wording\n\n🕰️ Timelessness & Legacy – but not always via parenthood/family framing\n\nWhen family is the focus, vary your emotional angle (joy, strength, nostalgia, etc.) and avoid repetitive tones.\n\n🗓️ Seasonal Strategy (SeasonStage = {{ $json.SeasonStage }})\nUse SeasonStage to guide tone, emotion, and visual mood — not to mention the season name in every caption. Let seasonal cues emerge through imagery and energy, not explicit labels.\n\n✅ Use SeasonStage to:\n\nAdjust urgency and emotional pace (e.g., “final weeks to book”)\n\nShape imagery and mood (e.g., “golden evening light” vs. “sun-drenched beach”)\n\n❌ Do not:\n\nMention the season name (“summer,” “spring,” etc.) in every caption\n\nRepeat seasonal clichés (“As summer begins…” “This spring…”)\n\n⚠️ Limit explicit seasonal mentions to once every 4–5 captions unless the post is directly seasonal (e.g., Mother’s Day, holiday gifting)\n\n🧠 Emotional & Visual Hooks (optional ways to begin a post)\nYou may open a caption with:\n\nA vivid moment of interaction (“The way he reached for her hand mid-laugh…”)\n\nA home setting or design cue (“This foyer now holds three generations of smiles.”)\n\n\n🖼️ Visual Description Guidelines\n\nYou may describe the post’s format if relevant — e.g., “quote overlay,” “side-by-side transformation,” or “carousel of three candid moments.”\n\nAvoid values/emotions like “precious,” “timeless,” “beautiful connection”\n\nDescribe exactly what’s visible:\n\n“Client reviewing prints in the studio”\n\n“Framed black-and-white portrait above the fireplace”\n\n“Photo collage with a quote overlay”\n\nKeep descriptions brief, neutral in tone, and focused on layout or subject matter.\n\n🏷️ Hashtag Rules\nEnd every post with 13–15 hashtags:\n\nAlways include ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\n\nAdd 2–3 custom/local hashtags based on content or location:\n\ne.g., #NJbeachportraits, #hobokenweekend, #hobokenstudio"
},
"promptType": "define"
},
"typeVersion": 1.8
},
{
"id": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
"name": "Bucle sobre elementos",
"type": "n8n-nodes-base.splitInBatches",
"position": [
140,
-40
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
"name": "Sin operación, no hacer nada",
"type": "n8n-nodes-base.noOp",
"position": [
460,
-260
],
"parameters": {},
"typeVersion": 1
},
{
"id": "ae777421-894a-4013-8278-c6ff1c150e10",
"name": "Esperar",
"type": "n8n-nodes-base.wait",
"position": [
1620,
-20
],
"webhookId": "b5167d12-ee8b-49a0-836b-944b40e1a32a",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
"name": "Combinar",
"type": "n8n-nodes-base.merge",
"position": [
-440,
-40
],
"parameters": {},
"typeVersion": 3.1
},
{
"id": "e2f55596-4f66-437e-8509-4f3714285b65",
"name": "referencia",
"type": "@n8n/n8n-nodes-langchain.toolCode",
"position": [
940,
200
],
"parameters": {
"name": "Description_Reference",
"jsCode": "const references = [\n {\n title: \"Framed Portrait in Home\",\n description: \"Framed portrait hanging in a cozy home interior, placed above a mantel or sofa.\",\n themes: [\"Framing\", \"Interior\", \"Wall Art\"]\n },\n {\n title: \"Framing Overlay\",\n description: \"A blurred frame photo with a text overlay that says 'custom framing.'\",\n themes: [\"Framing\", \"Product\", \"Behind the Scenes\"]\n },\n {\n title: \"Father’s Day Collage\",\n description: \"A collage of 2–3 photos with 'Happy Father’s Day' text on top.\",\n themes: [\"Father’s Day\", \"Holiday\"]\n },\n {\n title: \"Testimonial Quote\",\n description: \"Blurred image with a client review placed in elegant text overlay.\",\n themes: [\"Testimonial\", \"Client Quote\"]\n },\n {\n title: \"Mac Behind the Scenes\",\n description: \"Photo of Mac working during a shoot — camera in hand, lights or backdrops visible.\",\n themes: [\"Artist Insight\", \"Behind the Scenes\"]\n },\n {\n title: \"Beach Portrait Styling\",\n description: \"Carousel of beach portraits with text like 'what to wear' or 'color palette tips.'\",\n themes: [\"Beach\", \"Style Guide\", \"Educational\"]\n },\n {\n title: \"Studio Portrait\",\n description: \"Neutral-tone studio portrait with soft lighting and minimal background.\",\n themes: [\"Studio\", \"Interior\"]\n },\n {\n title: \"Summer Promo\",\n description: \"Ad-style post with text like 'Celebrate Summer' or 'Now Booking Beach Sessions.'\",\n themes: [\"Seasonal\", \"Summer\", \"Ad\"]\n },\n {\n title: \"Holiday Message\",\n description: \"One image with a decorative border and greeting for July 4th, New Year, etc.\",\n themes: [\"Holiday\", \"Seasonal\", \"Card\"]\n },\n {\n title: \"Consultation Snapshot\",\n description: \"A client and photographer reviewing prints or digital proofs in the studio.\",\n themes: [\"Client Experience\", \"Behind the Scenes\", \"Studio\"]\n },\n {\n title: \"Archival Printing\",\n description: \"Close-up of the printer or archival paper roll in use, highlighting quality.\",\n themes: [\"Printing\", \"Product\", \"Behind the Scenes\"]\n },\n {\n title: \"Custom Albums\",\n description: \"Photo of a hands flipping through a custom photo album on a wood table.\",\n themes: [\"Albums\", \"Products\", \"Details\"]\n },\n {\n title: \"Wall Display Mockup\",\n description: \"Mockup of a wall gallery with 2–4 portrait prints in a clean, staged room.\",\n themes: [\"Interior\", \"Framing\", \"Gallery\"]\n },\n {\n title: \"Tools of the Trade\",\n description: \"Flatlay of cameras, lenses, and accessories on a studio desk.\",\n themes: [\"Artist Insight\", \"Tools\", \"Studio\"]\n },\n {\n title: \"Gift Card Promo\",\n description: \"Photo of a wrapped gift with an HP card insert, styled like a holiday present.\",\n themes: [\"Gifts\", \"Promo\", \"Product\"]\n }\n];\n\n// Matching logic\nconst input = query.toLowerCase();\n\nlet match = references.find(ref =>\n ref.themes.some(theme => input.includes(theme.toLowerCase()))\n);\n\nif (!match) {\n match = references[Math.floor(Math.random() * references.length)];\n}\n\nreturn match.description;\n",
"description": "Help the AI Agent create a visual description of the post. Have it create one with a similar structure, clarity and simplicity. "
},
"typeVersion": 1.1
},
{
"id": "b96217b2-3298-4f1e-bb8f-601728ed5986",
"name": "Extraer título, descripción, url",
"type": "n8n-nodes-base.code",
"position": [
-1440,
180
],
"parameters": {
"jsCode": "const items = $input.all();\nconst output = [];\n\nfor (const item of items) {\n const metadata = item.json.metadata || {};\n const url = metadata.canonicalUrl || item.json.url || '';\n const title = metadata.title || '';\n const description = metadata.description || '';\n \n // Optional: Filter out any posts without a title\n if (title && url) {\n output.push({\n json: {\n URL: url,\n Title: title,\n Description: description\n }\n });\n }\n}\n\nreturn output;\n"
},
"typeVersion": 2
},
{
"id": "2d435a34-ac65-4d26-8c09-fe87afcc6e71",
"name": "Introducir mes preferido para blogs",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1240,
180
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Fujtlc6eM7k9s5AV",
"name": "Google Sheets account 3"
}
},
"typeVersion": 4.5
},
{
"id": "1390ac63-087a-49b0-b0ee-de7679160e36",
"name": "Rastrear entradas del blog",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1640,
180
],
"parameters": {
"options": {}
},
"typeVersion": 4.2
},
{
"id": "c1bce033-228d-44f0-a60e-26dc4068e696",
"name": "Esperar para evitar límite de solicitudes",
"type": "n8n-nodes-base.wait",
"position": [
-1020,
180
],
"webhookId": "a57b14a3-b7a8-4359-b3d7-aa69f72d1123",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "52b5306c-b81a-43b2-a778-63f86af543b0",
"name": "Leer mes preferido para blogs",
"type": "n8n-nodes-base.googleSheets",
"position": [
-820,
180
],
"parameters": {
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Fujtlc6eM7k9s5AV",
"name": "Google Sheets account 3"
}
},
"executeOnce": true,
"typeVersion": 4.5
},
{
"id": "e78d082b-711b-442a-aad3-b72b1a8b1744",
"name": "Leer datos",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1240,
-200
],
"parameters": {
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Fujtlc6eM7k9s5AV",
"name": "Google Sheets account 3"
}
},
"typeVersion": 4.5
},
{
"id": "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9",
"name": "Extraer información",
"type": "n8n-nodes-base.code",
"position": [
-200,
-40
],
"parameters": {
"jsCode": "const startDate = new Date(\"2025-07-07\");\nconst endDate = new Date(\"2026-01-30\");\n\nconst daysOfWeek = [1, 3, 5]; // Monday, Wednesday, Friday\nconst fallbackPillars = [\"Family Images\", \"Behind-the-Scenes\"];\nconst serviceSubThemes = [\n \"Beach\", \"Park\", \"Family\", \"Maternity\", \"Newborns\",\n \"Headshots\", \"Studio\", \"Beauty\", \"Frames\",\n \"Photo albums\", \"Cards\", \"Birthdays\", \"Anniversaries\"\n];\nconst processSubThemes = [\n \"Images of studio layout/exterior of studio\",\n \"Printer + archival inks\",\n \"Frame options/process\",\n \"Photo of camera/lighting equipment used to prove quality\",\n \"Photo presentation and selection\",\n \"Photo of consultation (blurred client + focus on HP employee)\"\n];\n\nconst items = $input.all();\nconst contentRows = items.filter(r => r.json.Pillar);\nconst blogPosts = items.filter(r => r.json.URL);\n\nconst output = [];\nconst lastUsedDates = {};\nconst serviceUsedMonths = {};\nconst processUsedMonths = {};\nlet serviceSubThemeIndex = 0;\nlet processSubThemeIndex = 0;\nlet lastFallbackUsed = null;\n\nfunction getSeasonStage(date) {\n const month = date.getMonth() + 1; // JavaScript months are 0-based\n\n if (month === 12 || month === 1 || month === 2) {\n if (month === 12) return \"Early Winter\";\n if (month === 1) return \"Mid Winter\";\n return \"Late Winter\"; // February\n }\n if (month === 3 || month === 4 || month === 5) {\n if (month === 3) return \"Early Spring\";\n if (month === 4) return \"Mid Spring\";\n return \"Late Spring\"; // May\n }\n if (month === 6 || month === 7 || month === 8) {\n if (month === 6) return \"Early Summer\";\n if (month === 7) return \"Mid Summer\";\n return \"Late Summer\"; // August\n }\n if (month === 9 || month === 10 || month === 11) {\n if (month === 9) return \"Early Fall\";\n if (month === 10) return \"Mid Fall\";\n return \"Late Fall\"; // November\n }\n}\n\n\nfunction getMinDays(freq) {\n const f = (freq || '').toLowerCase();\n if (f.includes('bi-weekly')) return 14;\n if (f.includes('weekly')) return 7;\n if (f.includes('daily')) return 1;\n if (f.includes('every 2 months')) return 60;\n if (f.includes('monthly') || f.includes('every 4 weeks')) return 30;\n return 14;\n}\n\nfunction daysBetween(d1, d2) {\n return Math.floor((d2 - d1) / (1000 * 60 * 60 * 24));\n}\n\nfunction isScheduledDay(date) {\n return daysOfWeek.includes(date.getDay());\n}\n\nfor (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {\n if (!isScheduledDay(d)) continue;\n\n const date = new Date(d);\n const dateStr = date.toISOString().split('T')[0];\n const dayName = date.toLocaleDateString('en-US', { weekday: 'long' });\n const seasonStage = getSeasonStage(date);\n const monthKey = `${date.getFullYear()}-${date.getMonth()}`;\n const currentMonthName = date.toLocaleString('default', { month: 'long' }).toLowerCase();\n let scheduled = false;\n\n // Blog logic\n const blog = blogPosts.find(b => {\n const preferredMonth = (b.json[\"Preferred Month\"] || '').toLowerCase();\n return preferredMonth === currentMonthName && !b.json._used;\n });\n\n if (blog) {\n blog.json._used = true;\n output.push({\n json: {\n Date: dateStr,\n Day: dayName,\n Pillar: \"Posts based on blogs\",\n Format: \"Images\",\n Description: blog.json.Description || '',\n Examples: `${blog.json.Title} — ${blog.json.URL}`,\n SeasonStage: seasonStage\n }\n });\n continue;\n }\n\n // Regular content\n for (const row of contentRows) {\n const basePillar = row.json.Pillar.trim();\n const freq = row.json.Frequency;\n const minGap = getMinDays(freq);\n const lastUsed = lastUsedDates[basePillar];\n const diff = lastUsed ? daysBetween(lastUsed, date) : Infinity;\n\n if (diff < minGap) continue;\n if (output.length > 0 && output[output.length - 1].json.Pillar === basePillar) continue;\n\n const post = JSON.parse(JSON.stringify(row.json)); // Deep copy\n let finalPillar = basePillar;\n\n if (basePillar.toLowerCase() === \"types of services offered\") {\n if (serviceUsedMonths[monthKey]) continue;\n const sub = serviceSubThemes[serviceSubThemeIndex % serviceSubThemes.length];\n finalPillar = `Types of services offered – ${sub}`;\n serviceUsedMonths[monthKey] = true;\n serviceSubThemeIndex++;\n }\n\n if (basePillar.toLowerCase() === \"explanation of processes (ex: presentation, printing, installation)\") {\n if (processUsedMonths[monthKey]) continue;\n const sub = processSubThemes[processSubThemeIndex % processSubThemes.length];\n finalPillar = `Explanation of processes – ${sub}`;\n processUsedMonths[monthKey] = true;\n processSubThemeIndex++;\n }\n\n output.push({\n json: {\n Date: dateStr,\n Day: dayName,\n Pillar: finalPillar,\n Format: post[\"Content Form\"] || post[\"Format\"] || '',\n Description: post[\"Structure\"] || '',\n Examples: post[\"Examples\"] || '',\n SeasonStage: seasonStage\n }\n });\n lastUsedDates[basePillar] = date;\n scheduled = true;\n break;\n }\n\n // Fallback logic\n if (!scheduled) {\n const options = fallbackPillars.filter(p => p !== lastFallbackUsed);\n const fallback = options[0] || fallbackPillars[0];\n output.push({\n json: {\n Date: dateStr,\n Day: dayName,\n Pillar: fallback,\n Format: '',\n Description: '',\n Examples: '',\n SeasonStage: seasonStage\n }\n });\n lastFallbackUsed = fallback;\n }\n}\n\nreturn output;\n"
},
"typeVersion": 2
},
{
"id": "4fd2f21e-636a-4d97-afde-76a735d76bbc",
"name": "Identificar festividad",
"type": "n8n-nodes-base.code",
"position": [
440,
-20
],
"parameters": {
"jsCode": "const post = $json;\nconst date = new Date(post.Date);\nconst year = date.getFullYear();\nconst month = date.getMonth(); // 0 = Jan, 11 = Dec\nconst dateNum = date.getDate();\nconst day = date.getDay(); // 0 = Sun, 1 = Mon, ..., 6 = Sat\n\nconst formattedDate = date.toISOString().split(\"T\")[0];\n\nfunction isNthWeekday(month, weekday, n) {\n let count = 0;\n for (let i = 1; i <= 31; i++) {\n const d = new Date(year, month, i);\n if (d.getMonth() !== month) break;\n if (d.getDay() === weekday) count++;\n if (count === n) return i;\n }\n return -1;\n}\n\nfunction isLastWeekday(month, weekday) {\n let last = -1;\n for (let i = 1; i <= 31; i++) {\n const d = new Date(year, month, i);\n if (d.getMonth() !== month) break;\n if (d.getDay() === weekday) last = i;\n }\n return last;\n}\n\n// Easter (hardcoded for accuracy)\nconst easterDates = {\n 2025: \"2025-04-20\",\n 2026: \"2026-04-05\",\n 2027: \"2027-03-28\"\n};\n\nlet holiday = \"None\";\n\n// Fixed-date holidays\nif (month === 0 && dateNum === 1) holiday = \"New Year’s Day\";\nelse if (month === 1 && dateNum === 14) holiday = \"Valentine’s Day\";\nelse if (month === 2 && dateNum === 17) holiday = \"St. Patrick’s Day\";\nelse if (formattedDate === easterDates[year]) holiday = \"Easter\";\nelse if (month === 5 && dateNum === 19) holiday = \"Juneteenth\";\nelse if (month === 6 && dateNum === 4) holiday = \"Independence Day\";\nelse if (month === 9 && dateNum === 11) holiday = \"Veterans Day\";\nelse if (month === 9 && dateNum === 31) holiday = \"Halloween\"; // rare edge case\nelse if (month === 11 && dateNum === 25) holiday = \"Christmas Day\";\n\n// Floating holidays\nelse if (month === 0 && dateNum === isNthWeekday(0, 1, 3)) holiday = \"Martin Luther King Jr. Day\";\nelse if (month === 1 && dateNum === isNthWeekday(1, 1, 3)) holiday = \"Presidents’ Day\";\nelse if (month === 4 && dateNum === isNthWeekday(4, 0, 2)) holiday = \"Mother’s Day\";\nelse if (month === 4 && dateNum === isLastWeekday(4, 1)) holiday = \"Memorial Day\";\nelse if (month === 5 && dateNum === isNthWeekday(5, 0, 3)) holiday = \"Father’s Day\";\nelse if (month === 8 && dateNum === isNthWeekday(8, 1, 1)) holiday = \"Labor Day\";\nelse if (month === 9 && dateNum === isNthWeekday(9, 1, 2)) holiday = \"Columbus Day\";\nelse if (month === 10 && dateNum === isNthWeekday(10, 4, 4)) holiday = \"Thanksgiving\";\n\npost.Holiday = holiday;\nreturn { json: post };\n"
},
"typeVersion": 2
},
{
"id": "a097c38c-d64e-41e9-92db-206304a7b62d",
"name": "Título, Descripción, Hashtags",
"type": "n8n-nodes-base.code",
"position": [
1160,
-20
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const fullText = $json.output || '';\nconst lines = fullText.split('\\n');\n\nlet caption = '';\nlet visualDescription = '';\nlet hashtags = '';\n\nlet currentSection = '';\n\nfor (const line of lines) {\n const trimmed = line.trim();\n\n // Normalize section header detection (handles : — or none)\n if (/^\\*?\\*?\\s*Caption\\b/i.test(trimmed)) {\n currentSection = 'caption';\n continue;\n } else if (/^\\*?\\*?\\s*Visual Description\\b/i.test(trimmed)) {\n currentSection = 'visualDescription';\n continue;\n } else if (/^\\*?\\*?\\s*Hashtags\\b/i.test(trimmed)) {\n currentSection = 'hashtags';\n continue;\n }\n\n if (currentSection === 'caption') caption += trimmed + ' ';\n else if (currentSection === 'visualDescription') visualDescription += trimmed + ' ';\n else if (currentSection === 'hashtags') hashtags += trimmed + ' ';\n}\n\nreturn {\n json: {\n Caption: caption.trim(),\n Description: visualDescription.trim(),\n Hashtags: hashtags.trim()\n }\n};\n"
},
"typeVersion": 2
},
{
"id": "90e6800b-0abe-4389-9368-41ce6b1bec3e",
"name": "Renombrar campos",
"type": "n8n-nodes-base.set",
"position": [
1380,
-20
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cc70084f-cad3-45d8-a680-09a59fa6c87e",
"name": "Caption",
"type": "string",
"value": "={{ $json.Caption }}"
},
{
"id": "4e265422-f477-4a90-8043-ca6ede55d7e6",
"name": "Hashtags",
"type": "string",
"value": "={{ $json.Hashtags }}"
},
{
"id": "d16a69df-4b8e-464e-a19d-e8566c12897d",
"name": "Date",
"type": "string",
"value": "={{ $('Loop Over Items').item.json.Date }}"
},
{
"id": "7e52cc33-9c30-4f22-9260-d04d6f2d365d",
"name": "Day",
"type": "string",
"value": "={{ $('Loop Over Items').item.json.Day }}"
},
{
"id": "8b1956b4-e34a-46c0-9e9e-fe21a8b49ffc",
"name": "Pillar",
"type": "string",
"value": "={{ $('Loop Over Items').item.json.Pillar }}"
},
{
"id": "8a7356bc-3292-41e7-8f04-bc156e8090c0",
"name": "Format",
"type": "string",
"value": "={{ $('Loop Over Items').item.json.Format }}"
},
{
"id": "8bc6ddeb-71c2-419c-8be4-522cb7ad96e2",
"name": "Description",
"type": "string",
"value": "={{ $json.Description }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "04be4907-f32c-4551-a4c5-b8de83c1b511",
"name": "Exportar datos",
"type": "n8n-nodes-base.googleSheets",
"position": [
1860,
-20
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Fujtlc6eM7k9s5AV",
"name": "Google Sheets account 3"
}
},
"typeVersion": 4.5
},
{
"id": "1277c53a-64bc-4dd6-a100-497a37ad22d7",
"name": "Nota adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3180,
-960
],
"parameters": {
"width": 1080,
"height": 2060,
"content": "## Try It Out!\n\n### This n8n template creates a fully automated Instagram content schedule using AI and Google Sheets. It is perfect for content creators, marketing teams, or local businesses looking to organize and scale their social media posting.\n\n### How it works\nThe workflow starts by reading two sets of inputs from a Google Sheet:\n- Your content strategy inputs (Pillar, Objective, Frequency, Format, Structure, Examples).\n- A list of scraped blog posts with title, URL, and description (fetched from your website).\n- Blog posts are scraped using Apify and parsed to extract key fields, which are stored in a tab labeled \"Input (blog month)\".\n- You can assign a preferred posting month for each blog (e.g. fall blog posts get tagged for September).\n- The workflow then merges both inputs and extracts the relevant information for further information added by ChatGPT.\n\n### AI Scheduling & Personalization\nOnce merged, the workflow loops through each content item and:\n- Identifies if the scheduled post falls on or near a holiday (like Mother’s Day) and adjusts the content accordingly.\n- A reference tool is attached to guide structure and tone, based on a library of post examples.\n- Sends the content to an AI Agent (using GPT-4, but customizable) that generates:\n1. A compelling Instagram caption\n2. A visual description\n3. Hashtags\n4. Suggested post date, day, content pillar, and format (carousel, reel, image, etc.)\n\n### Output\n- All generated content including captions, structure, dates, hashtags, and pillar is exported into a tab titled Output in your Google Sheet.\n- The final schedule is ready for manual review, editing, or publishing to social media.\n\n### How to use\n- The workflow uses a manual trigger to start, but you can replace it with a Webhook, cron job, or form submission.\n- Add/edit your content strategy in Google Sheets.\n\n### How to Set-Up\n### Initial Input Tab\n- Define your content pillars and structure\n- Create a tab named \"Input\" or \"Strategy\" \nInclude these columns:\n- Pillar: e.g., Family images\n- Objective: e.g., Showcase images\n- Frequency: e.g., Bi-weekly\n- Content Form: e.g., Images, Reels\n- Structure: brief description of expected layout (e.g., carousel Q&A, singular photo)\n- Examples: prompts or questions to guide AI (e.g., Why do you think families should do a session?)\n\n### Input (blog month) Tab – Store scraped blog content\nInclude these columns:\n- URL: direct link to blog post\n- Title: blog post title\n- Description: short summary of the post\n- Preferred Month: month you want it posted (e.g., August, September)\n- This sheet is partially auto-filled by the workflow (except for Preferred Month)\n\n### Output Tab – Final scheduled content\nInclude these columns:\n- Date: scheduled posting date (YYYY-MM-DD)\n- Day: day of the week\n- Pillar: content category assigned\n- Format: e.g., Images, Reels, Carousel\n- Description: visual summary\n- Caption: Instagram-ready caption\n- Hashtags: complete hashtag block\n\n### To use the Apify HTTP Request node:\n1. Drag in an HTTP Request node into your n8n workflow.\n2. Set the Method and URL based on how you're using Apify:\n- Use POST if you want to run an actor live with dynamic input (e.g. scrape blog posts in real time).\n- Use GET if you want to retrieve results from a completed or static dataset run (faster and cheaper if you're reusing previous data).\n3. Configure query or body parameters:\n4. Include your Apify API token for authentication (e.g. token=YOUR_API_KEY)\n- For POST: include an input object with any required actor settings (e.g., blog URL to scrape).\n- For GET: specify the dataset ID in the URL \n5. Test the node to ensure you're retrieving the blog titles, descriptions, and URLs as expected.\n\n### Requirements\n- Apify account for scraping blog posts\n- OpenAI key (e.g. GPT-4) or another model of your choice\n- Google Sheets Credentials \n\n### Example Use Cases\n- A photographer repurposing blogs into Instagram carousels\n- A nonprofit automatically generating seasonal posts \n- A small team managing multi-pillar content across weeks or months\n\n### Need Help?\nJoin the n8n Discord or ask in the n8n Forum!\nHappy Content Making ! 📅✨\n"
},
"typeVersion": 1
},
{
"id": "fac1861d-edea-4450-bae2-d2a4cc4a3ef5",
"name": "Nota adhesiva6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2020,
-480
],
"parameters": {
"color": 7,
"width": 1960,
"height": 980,
"content": "# 1. Two Sets of Input\n\nSet #1 (top): Reads the initial input data that is manually inputted of the type of pillar(s) you want to focus on in your content, it's objective, their frequency, form of content, structure, and examples. \n\n\nSet #2 (bottom): The Apify actor scrapes websites for their blog post to repurpose as social media content. Next, the code node will extract the title, description and URL. You can manually input the preferred month you want the post posted and the workflow will take that into account when creating the schedule. \n\nThe information from these two sets of input will merge and eventually, all the relevant information will be extracted for further analysis. "
},
"typeVersion": 1
},
{
"id": "c0cd04bd-7a71-4250-abe8-369e547956bf",
"name": "Nota adhesiva7",
"type": "n8n-nodes-base.stickyNote",
"position": [
60,
-480
],
"parameters": {
"color": 7,
"width": 1960,
"height": 980,
"content": "# 2. Use GPT-4 to Create Captions, Hashtags, and Visual Descriptions\n\nUsing the Code Node \"Identify Holiday\", the AI will tailor a post accordingly if it falls on the day of a holiday. For example, if a post falls on Mother's Day, it may use that to set the scene for the rest of the post. The OpenAI Agent will create the visual description, hashtag, caption, and date and day it should be posted. The AI takes into account the examples listed earlier in the Google Sheets so if you want to tweak the results, that would be a good start.\n\nLastly, after the AI Agent has run, all of the data is exported into a google sheets tab labelled \"Output\". "
},
"typeVersion": 1
},
{
"id": "18c9f341-d123-47d8-a049-e0a41c6ff8d8",
"name": "Nota adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2020,
-1320
],
"parameters": {
"width": 1880,
"height": 800,
"content": "## Input #1: \"Initial Input\" \n\n\n"
},
"typeVersion": 1
},
{
"id": "447abe53-d03d-4905-b1f8-e69991210d11",
"name": "Nota adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2020,
560
],
"parameters": {
"width": 1880,
"height": 440,
"content": "## Input #2: \"Input (blog month)\" \n\n\n"
},
"typeVersion": 1
},
{
"id": "eb712852-4cb7-4618-9437-edc2aa203017",
"name": "Nota adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2080,
-460
],
"parameters": {
"width": 1860,
"height": 580,
"content": "## Output \n\n\n"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "cf50b336-182f-4027-9c8c-611c2d4e599c",
"connections": {
"ae777421-894a-4013-8278-c6ff1c150e10": {
"main": [
[
{
"node": "04be4907-f32c-4551-a4c5-b8de83c1b511",
"type": "main",
"index": 0
}
]
]
},
"8f2da55a-4f97-4c80-beaa-2f6877d5ad36": {
"main": [
[
{
"node": "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9",
"type": "main",
"index": 0
}
]
]
},
"e78d082b-711b-442a-aad3-b72b1a8b1744": {
"main": [
[
{
"node": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
"type": "main",
"index": 0
}
]
]
},
"e2f55596-4f66-437e-8509-4f3714285b65": {
"ai_tool": [
[
{
"node": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
"type": "ai_tool",
"index": 0
}
]
]
},
"04be4907-f32c-4551-a4c5-b8de83c1b511": {
"main": [
[
{
"node": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
"type": "main",
"index": 0
}
]
]
},
"90e6800b-0abe-4389-9368-41ce6b1bec3e": {
"main": [
[
{
"node": "ae777421-894a-4013-8278-c6ff1c150e10",
"type": "main",
"index": 0
}
]
]
},
"cc5be169-0f5f-4ff1-8cea-11bb1325970a": {
"main": [
[
{
"node": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
"type": "main",
"index": 0
}
],
[
{
"node": "4fd2f21e-636a-4d97-afde-76a735d76bbc",
"type": "main",
"index": 0
}
]
]
},
"4fd2f21e-636a-4d97-afde-76a735d76bbc": {
"main": [
[
{
"node": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
"type": "main",
"index": 0
}
]
]
},
"1390ac63-087a-49b0-b0ee-de7679160e36": {
"main": [
[
{
"node": "b96217b2-3298-4f1e-bb8f-601728ed5986",
"type": "main",
"index": 0
}
]
]
},
"ed563c5f-0c00-4c5e-9ed8-fed7d329cce9": {
"main": [
[
{
"node": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
"type": "main",
"index": 0
}
]
]
},
"8d8bb837-d650-488c-bb03-ce865fba2a6e": {
"main": [
[]
]
},
"52b5306c-b81a-43b2-a778-63f86af543b0": {
"main": [
[
{
"node": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
"type": "main",
"index": 1
}
]
]
},
"2d435a34-ac65-4d26-8c09-fe87afcc6e71": {
"main": [
[
{
"node": "c1bce033-228d-44f0-a60e-26dc4068e696",
"type": "main",
"index": 0
}
]
]
},
"b220a61b-3bce-49db-a5cc-0b56e0480f39": {
"main": [
[
{
"node": "a097c38c-d64e-41e9-92db-206304a7b62d",
"type": "main",
"index": 0
}
]
]
},
"c1bce033-228d-44f0-a60e-26dc4068e696": {
"main": [
[
{
"node": "52b5306c-b81a-43b2-a778-63f86af543b0",
"type": "main",
"index": 0
}
]
]
},
"a097c38c-d64e-41e9-92db-206304a7b62d": {
"main": [
[
{
"node": "90e6800b-0abe-4389-9368-41ce6b1bec3e",
"type": "main",
"index": 0
}
]
]
},
"b96217b2-3298-4f1e-bb8f-601728ed5986": {
"main": [
[
{
"node": "2d435a34-ac65-4d26-8c09-fe87afcc6e71",
"type": "main",
"index": 0
}
]
]
},
"99e295bb-f026-4641-a96a-098461e70cf7": {
"main": [
[
{
"node": "e78d082b-711b-442a-aad3-b72b1a8b1744",
"type": "main",
"index": 0
},
{
"node": "1390ac63-087a-49b0-b0ee-de7679160e36",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Creación de contenido, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
keisha kalra
@keishaCompartir este flujo de trabajo