Modèle HP - Planification de contenu

Avancé

Ceci est unContent Creation, Multimodal AIworkflow d'automatisation du domainecontenant 24 nœuds.Utilise principalement des nœuds comme Set, Code, Wait, Merge, HttpRequest. Planification automatique de contenu Instagram avec GPT-4, Apify et Google Sheets

Prérequis
  • Peut nécessiter les informations d'identification d'authentification de l'API cible
  • Informations d'identification Google Sheets API
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "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": "Lors du clic sur 'Tester le workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1960,
        40
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
      "name": "Agent IA - planification de contenu",
      "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": "Boucler sur les éléments",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        140,
        -40
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
      "name": "Aucune opération, ne rien faire",
      "type": "n8n-nodes-base.noOp",
      "position": [
        460,
        -260
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ae777421-894a-4013-8278-c6ff1c150e10",
      "name": "Attendre",
      "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": "Fusionner",
      "type": "n8n-nodes-base.merge",
      "position": [
        -440,
        -40
      ],
      "parameters": {},
      "typeVersion": 3.1
    },
    {
      "id": "e2f55596-4f66-437e-8509-4f3714285b65",
      "name": "référence",
      "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": "Extraire titre, description, 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": "Saisir le mois de blog préféré",
      "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": "Scraper les articles de blog",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1640,
        180
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c1bce033-228d-44f0-a60e-26dc4068e696",
      "name": "Attendre pour éviter la limite de requêtes",
      "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": "Lire le mois de blog préféré",
      "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": "Lire les données",
      "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": "Extraire les informations",
      "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": "Identifier les jours fériés",
      "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": "Légende, Description, 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": "Renommer les champs",
      "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": "Exporter les données",
      "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": "Note autocollante",
      "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": "Note autocollante6",
      "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": "Note autocollante7",
      "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": "Note autocollante1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        -1320
      ],
      "parameters": {
        "width": 1880,
        "height": 800,
        "content": "## Input #1: \"Initial Input\" \n\n![Workflow Screenshot](https://i.imgur.com/UTa0VCH.png)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "447abe53-d03d-4905-b1f8-e69991210d11",
      "name": "Note autocollante2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        560
      ],
      "parameters": {
        "width": 1880,
        "height": 440,
        "content": "## Input #2: \"Input (blog month)\" \n\n![Google Sheets Strategy Tab](https://i.imgur.com/g1GYuU7.png)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "eb712852-4cb7-4618-9437-edc2aa203017",
      "name": "Note autocollante3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        -460
      ],
      "parameters": {
        "width": 1860,
        "height": 580,
        "content": "## Output \n![Blog Input Tab](https://i.imgur.com/onDxG3U.png)\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
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Avancé - Création de contenu, IA Multimodale

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Workflows recommandés

Modèle - Génération de blog
Utiliser l'auto-complétion de Google et GPT-4 pour générer des articles de blog optimisés SEO
Set
Code
Wait
+
Set
Code
Wait
21 Nœudskeisha kalra
Création de contenu
Automatisation de courts métrages sans visage avec OpenAI, RunwayML et ElevenLabs
Automatisation de courts métrages sans visage : du script aux réseaux sociaux avec OpenAI, RunwayML et ElevenLabs
Set
Code
Wait
+
Set
Code
Wait
56 NœudsLeeWei
Création de contenu
Automatisation de la création de contenu viral avec OpenAI, ElevenLabs et Fal.ai pour les vidéos, les podcasts et l'ASMR
Automatiser la création de contenu viral pour la vidéo, les podcasts et l'ASMR avec OpenAI, ElevenLabs et Fal.ai
Set
Code
Wait
+
Set
Code
Wait
97 NœudsAdam Crafts
Création de contenu
Génération de masse de contenu SEO et création de brouillons Webflow avec images IA (modèle)
Génération de contenu SEO en masse avec GPT, images Gemini et brouillons Webflow
If
Set
Code
+
If
Set
Code
54 NœudsDahiana
Création de contenu
Convertir les points de douleur commerciaux Reddit en contenu viral LinkedIn avec l'IA et Freepik
Utiliser l'IA et Freepik pour convertir les points de douleur métier Reddit en contenu LinkedIn viral
If
Set
Code
+
If
Set
Code
48 NœudsDaniel Lianes
Création de contenu
💥 Automatisation des publicités vidéo avec NanoBanana, Seedream 4, ChatGPT Image et Veo 3 - VIDE
Utiliser l'IA (NanoBanana, Seedream, GPT-4o, Veo 3) pour automatiser et publier des campagnes publicitaires vidéo
Set
Code
Wait
+
Set
Code
Wait
63 NœudsDr. Firas
Création de contenu
Informations sur le workflow
Niveau de difficulté
Avancé
Nombre de nœuds24
Catégorie2
Types de nœuds12
Description de la difficulté

Adapté aux utilisateurs avancés, avec des workflows complexes contenant 16+ nœuds

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34