{
  "id": "TIbvOPxs6gMrf1sa",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Community Challenge April \u2014 Submit",
  "tags": [],
  "nodes": [
    {
      "id": "a358c702-b02b-4820-8b83-b3749c870c1d",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -2192,
        1280
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "237c21ad-541c-4cc2-9854-d4defe4fc02b",
      "name": "Marcus \u2014 Merge Context",
      "type": "n8n-nodes-base.set",
      "position": [
        -1296,
        1040
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9bbe24b7-e5f3-4046-9339-7864bbb71b93",
              "name": "brief_id",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Brief ID'] }}"
            },
            {
              "id": "c4",
              "name": "client",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.client }}"
            },
            {
              "id": "c1",
              "name": "brand_name",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.brand_name }}"
            },
            {
              "id": "c2",
              "name": "brand_voice",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.brand_voice }}"
            },
            {
              "id": "c3",
              "name": "output_channel",
              "type": "string",
              "value": "google_sheets"
            },
            {
              "id": "c6",
              "name": "content_type",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Content Type'] }}"
            },
            {
              "id": "c7",
              "name": "topic_hint",
              "type": "string",
              "value": "={{ $json['Topic Hint'] || '' }}"
            },
            {
              "id": "c8",
              "name": "tone_note",
              "type": "string",
              "value": "={{ $json['Tone Note'] || '' }}"
            },
            {
              "id": "c5",
              "name": "platform",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json.Platform }}"
            },
            {
              "id": "c9",
              "name": "lastFeedback",
              "type": "string",
              "value": "={{ $json.lastFeedback || '' }}"
            },
            {
              "id": "6890e10e-e2b6-46b6-9882-d959214f691d",
              "name": "tone_platform",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.tone_platform }}"
            },
            {
              "id": "20cc793b-10c4-483a-972a-94e27e366c78",
              "name": "language_avoid ",
              "type": "string",
              "value": "={{ $('Client DB Lookup (Sheets)').item.json.language_avoid }}"
            },
            {
              "id": "45704602-59b0-46c8-bdd1-539c464b761d",
              "name": "visual_style",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.visual_style }}"
            },
            {
              "id": "442141c6-ff8f-400c-b86d-2e56aefeb8d7",
              "name": "key_messages",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.key_messages }}"
            },
            {
              "id": "71bb1cea-e0ff-4e5e-8409-0b65074864ba",
              "name": "approved_angle",
              "type": "string",
              "value": "={{ $json['Approved angle'] }}"
            },
            {
              "id": "b1c7ace2-21a6-476b-8f4f-d03aa09528b1",
              "name": "marcus_iteration",
              "type": "number",
              "value": "={{ $json.marcus_iteration || 0 }}"
            },
            {
              "id": "6b52f359-789b-4be4-8fe7-6b0ac19d64a3",
              "name": "brand_guide",
              "type": "string",
              "value": "--- category: n8n area: resource project: community-challenge-april status: active tags: [brand, loopin, social-media] created: 2026-04-28 updated: 2026-04-28 ---  # Loopin \u2014 Brand Guide  _This is the client brief Relay uses when creating content for Loopin.  ---  ## About Loopin  Loopin is a tool for teams that replaces unnecessary meetings with short, async video updates. Instead of scheduling a call, team members record a quick video and share it when it suits them.  Teams use Loopin for standups, project updates, onboarding, feedback, handovers, and quick explanations.  **Founded:** 2022   **Stage:** Series A, ~80 employees   **Headquarters:** Amsterdam (fully remote team)  ---  ## Target audience  Loopin's content is aimed at decision-makers and team leads at growing SaaS startups.  | Dimension | Description | |---|---| | **Company size** | 20\u2013200 employees | | **Work model** | Remote or hybrid | | **Role** | Engineering managers, product leads, ops managers, founders | | **Pain point** | Too many meetings, poor async habits, slow feedback loops | | **Goal** | Stay aligned without being pulled into more calls |  **The person Loopin writes for** is someone who already believes meetings are broken. They are not waiting to be convinced \u2014 they want validation and practical help changing how their team works.  ---  ## Brand voice  | Attribute | What it means in practice | |---|---| | **Clear** | Every sentence earns its place. If you can remove a word without losing meaning, remove it. | | **Direct** | No hedging. Say what you mean. \"Loopin cuts meeting time\" not \"Loopin can potentially help reduce meeting overhead.\" | | **Human** | Written by real people for real people. Not corporate-speak, not over-polished. | | **Practical** | Every post gives the reader something useful: a tip, a frame, a perspective they can act on. | | **Confident** | Loopin has a point of view. Async is better for focused work. We say that plainly, not defensively. |  ---  ## Tone by platform  | Platform | Tone | |---|---| | **LinkedIn** | Thoughtful and professional. Still clear and direct, but more considered \u2014 longer sentences are fine when they add value. Thought leadership, data, and team stories work best here. | | **X (Twitter)** | Sharp and punchy. Strong opinions, short sentences. Use numbers and contrast. Threads work well for multi-part ideas. | | **Instagram** | Warm and visual. The image carries the message. Caption adds colour and context. This is where team culture and behind-the-scenes content lives. |  ---  ## Key messages  These are the ideas Loopin comes back to across all platforms.  1. **Meetings are a default, not a necessity.** Most syncs can be replaced by a short async update. 2. **Async is not slower \u2014 it's calmer.** People do better work when they control their time. 3. **Teams that communicate async move faster.** Less interruption = more deep work = faster output. 4. **Loopin makes async easy.** Quick to record, easy to share, simple to review.  ---  ## Content pillars  | Pillar | Description | Examples | |---|---|---| | **Meeting culture** | Challenge the assumption that meetings are necessary | \"5 types of meetings that should be a video\" | | **Async tips** | Practical advice for teams switching to async work | \"How to run a standup without a Zoom call\" | | **Product** | Feature highlights and product updates (not salesy \u2014 show the value) | \"New feature: Digest view \u2014 catch up on what matters in 5 min\" | | **Social proof** | Customer stories, quotes, and use cases | \"How Monzo reduced standup time by 60% using async video\" | | **Team culture** | Loopin's own remote culture \u2014 how they practice what they preach | \"Our team of 80 has zero recurring meetings\" |  ---  ## Language guide  ### Use  - Async, asynchronous - Video update, quick video, short video - Team alignment, stay aligned - Deep work, focused time - Record once, watch anytime - Cut the call, skip the meeting - On your own schedule - Remote-first, hybrid  ### Avoid  | Avoid | Why | |---|---| | \"Hop on a call\" | Contradicts the whole brand | | \"Quick sync\" | Implies a meeting, which is what Loopin replaces | | \"Touch base\" | Generic corporate speak | | \"Circle back\" | Same \u2014 tired, corporate | | \"Leverage\" | Jargon, not human | | \"Streamline\" / \"Optimize\" / \"Utilise\" | Over-used B2B filler | | \"Transform your workflow\" | Too generic, sounds like everyone else | | \"Revolutionary\" / \"Game-changing\" | Overpromises, sounds insecure | | \"Solutions\" (as a standalone noun) | Vague and hollow | | Passive voice where active is possible | \"Updates are recorded\" \u2192 \"You record updates\" |  ---  ## Visual identity  ### Colours  | Role | Hex | Use | |---|---|---| | Primary | `#5B4AE8` | Main brand colour \u2014 buttons, key UI elements, overlays | | Secondary | `#00D9C8` | Accent highlights, icons, supporting elements | | Warm accent | `#FF5A1F` | CTAs, highlights, energy moments | | Light background | `#F3F4F6` | Cards, neutral fills | | Dark | `#111827` | Text on light backgrounds | | White | `#FFFFFF` | Main background |  ### Typography  - **Headings:** Inter Bold or similar geometric sans-serif - **Body:** Inter Regular - Clean and modern. No serif fonts in digital content.  ### Image style  **What works:**  - Real people in real work environments (home offices, co-working, casual settings) - Laptop or phone screens - Small teams collaborating - Natural light, neutral tones, clean backgrounds - Close-up of someone recording a short video on their laptop - Abstract illustrations with the brand colour palette (no clip art)  **What to avoid:**  - Stock photo clich\u00e9s: people in suits pointing at whiteboards, handshakes, overly staged meetings - Overly bright or saturated photos that feel generic - Crowded group photos that look like corporate stock imagery - Dark or moody aesthetics \u2014 this is a calm, clear brand - Zoom call screenshots or anything that implies meeting culture  ### Platform-specific visual dimensions  | Platform | Format | Dimensions | |---|---|---| | LinkedIn | Single image or carousel | 1200 \u00d7 627 px (landscape) or 1080 \u00d7 1080 px (square) | | X (Twitter) | Single image | 1600 \u00d7 900 px (landscape) or 1080 \u00d7 1080 px (square) | | Instagram | Feed post | 1080 \u00d7 1080 px (square) or 1080 \u00d7 1350 px (portrait) |  ### Text on images  - Keep it minimal. One headline line maximum. - Use white or `#111827` text depending on background. - Always leave enough contrast for readability. - Never put small body text directly on a photo background.   ---  ### Composition patterns for AI image generation  Use these patterns when generating Marcus's image prompts.  **Stat graphic** - Use for data-driven posts  - One large number or percentage in the centre - Small label below the number (e.g. \"of an exec's week spent in meetings\") - Loopin wordmark or \"Loopin.co\" in small text at the bottom right - Subtle geometric shape in the background (a circle or horizontal bar in the secondary teal) - Aspect ratio: 1080 \u00d7 1080 px (square)  **Product screenshot** - Use for product updates and feature announcements - Loopin UI mockup (dark mode, as described above) shown on a laptop or monitor - Placed at an angle (3/4 view) on a light background (`#F3F4F6`) with subtle shadow - One short text line at the top: the feature name or benefit - Aspect ratio: 1200 \u00d7 627 px (landscape for LinkedIn, 1080 \u00d7 1080 for square)  **People/culture photo** - Use for team culture posts - Real-looking photo aesthetic - Natural light, neutral tones, warm but not oversaturated - Optional overlay: thin colour block in primary purple with a 1\u20132 word label (e.g. \"async first\") - Aspect ratio: 1080 \u00d7 1080 px or 1080 \u00d7 1350 px portrait  **Carousel slide** - Use for how-to and multi-step posts - Slide 1: Title slide \u2014 large bold text on primary purple (`#5B4AE8`) background, white text, secondary teal accent line at the bottom - Slides 2\u2013N: White background, numbered heading in primary purple, 2\u20133 lines of body text in dark (`#111827`), optional small icon or illustration on the right - Slide N+1 (final): CTA slide \u2014 same style as title slide - Aspect ratio: 1080 \u00d7 1080 px per slide  ---  ## Brand do's and don'ts  | Do | Don't | |---|---| | Take a clear side (async > sync) | Hedge or try to appeal to everyone | | Use real numbers when possible | Make vague claims without evidence | | Address one idea per post | Try to say everything at once | | Write for the decision-maker who agrees | Write for the sceptic who needs convincing | | Show, don't tell (product screenshots, results) | Just tell people what Loopin does | | Write how a smart person talks | Write how a marketing deck reads |"
            },
            {
              "id": "29f6f403-fe16-452d-974a-100dc709ee96",
              "name": "sofia_angles ",
              "type": "string",
              "value": "={{ $('Sofia Save Angles').item.json.sofia_angles }}"
            },
            {
              "id": "8193aa79-ca3e-4abd-9d67-519c12c03cfd",
              "name": "platform_specs",
              "type": "string",
              "value": "--- category: n8n area: resource project: community-challenge-april status: active tags: [platform, social-media, relay, linkedin, twitter, instagram] created: 2026-04-28 updated: 2026-04-28 ---  # Relay \u2014 Platform Playbook  _This is the internal guide Relay uses when creating social media content. It defines the rules, limits, and best practices for each platform. Use this alongside the client brand guide when building posts._  ---  ## Overview  Relay currently publishes on three platforms for B2B clients like Loopin.  | Platform | Primary purpose | Best content types | |---|---|---| | **LinkedIn** | Thought leadership, reach, lead generation | Long-form insights, product posts, customer stories, data | | **X (Twitter)** | Real-time engagement, brand personality | Hot takes, threads, tips, quick product updates | | **Instagram** | Brand awareness, culture, visual identity | Team culture, behind-the-scenes, product UI, carousels |  ---  ## LinkedIn  ### Why it matters for Loopin  LinkedIn is Loopin's primary channel for reaching decision-makers at SaaS companies. Engineering managers, product leads, and founders scroll here during work hours. This is where trust and thought leadership gets built.  ### Format specs  | Element | Spec | |---|---| | **Caption length** | Up to 3,000 characters, but aim for 150\u2013600 words for best reach | | **Optimal post length** | 800\u20131,500 characters for text-only posts | | **Image dimensions** | 1200 \u00d7 627 px (landscape) or 1080 \u00d7 1080 px (square) | | **Carousel** | Up to 300 pages as PDF; 1080 \u00d7 1080 px per slide | | **Video** | Up to 10 minutes; square (1:1) or landscape (16:9) | | **Hashtags** | 3\u20135, placed at the end of the caption | | **Hook line** | Critical \u2014 LinkedIn cuts off after 2\u20133 lines; the hook determines whether someone clicks \"see more\" |  ### Best practices for Loopin  - **Open with a hook that stands alone.** The first line must be interesting enough on its own to earn the \"see more\" click. Use a bold statement, a surprising number, or a direct question. - **Use line breaks liberally.** Short paragraphs. One idea per block. White space increases readability on mobile. - **Tell a story or make a point.** The best-performing LinkedIn posts either tell a story with a lesson, or make a clear argument with evidence. - **End with one clear CTA.** A question to comment on, a link, or a prompt to share. Not all three. - **Avoid engagement bait.** \"Like if you agree\" or \"comment YES if this resonates\" \u2014 LinkedIn penalises it.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Thought leadership** | Text post with image | 1 clear opinion, evidence, conclusion | | **Product update** | Text + image or carousel | Feature benefit first, feature name second | | **Customer story** | Text post | Problem \u2192 what they did \u2192 result | | **Data post** | Text + stat graphic | Lead with the stat, explain the implication | | **How-to** | Text post or carousel | Numbered steps, practical, skimmable |  ### Hook formulas that work  - \"Most [role] think [X]. They're wrong.\" - \"We [did something]. Here's what happened.\" - \"[Number] things I wish I knew before [situation].\" - \"The [counterintuitive insight] that changed how we [do X].\" - \"Stop doing [X]. Here's why.\"  ---  ## X (Twitter)  ### Why it matters for Loopin  X is where Loopin builds real-time brand personality and engages with the tech and startup community. It's less about lead gen and more about presence, credibility, and staying top-of-mind with an opinionated audience.  ### Format specs  | Element | Spec | |---|---| | **Tweet length** | 280 characters (verified users: 25,000, but keep it short) | | **Thread length** | Ideally 3\u20137 tweets; beyond 10 loses engagement sharply | | **Image dimensions** | 1600 \u00d7 900 px (landscape, 16:9) or 1080 \u00d7 1080 px (square) | | **Video** | Up to 2 minutes 20 seconds; square or landscape | | **Hashtags** | 0\u20131 per tweet for brand accounts; heavy hashtag use looks spammy here | | **Character overhead** | URLs count as 23 characters regardless of length |  ### Best practices for Loopin  - **One idea per tweet.** Don't try to say everything in one post. The constraint forces clarity. - **Be direct.** X rewards bluntness. \"Async beats sync for focused work\" not \"There are many ways in which asynchronous communication may provide benefits.\" - **Use numbers.** \"3 hours a week\" is more memorable than \"a lot of time.\" - **Start threads with the best line.** The opening tweet must stand alone. Everything after is a bonus. - **Engage with replies.** X's algorithm rewards back-and-forth. Build in a question to invite responses.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Hot take** | Single tweet | Bold opinion with one supporting reason | | **Quick tip** | Single tweet or short thread | Practical, numbered, easy to screenshot | | **Data point** | Tweet + image | Lead with the number, not the source | | **Thread** | 3\u20137 tweets | Expanded argument or how-to | | **Product update** | Single tweet + image | Short, benefit-led, link to learn more |  ### Tweet formulas that work  - \"[Bold claim]. [Evidence or example].\" - \"[Surprising stat]. [Why it matters].\" - \"If you [belief], you probably [consequence].\" - \"The secret to [X]: [unexpected answer].\" - Thread opener: \"I [did X]. Here's everything I learned: [thread]\"  ---  ## Instagram  ### Why it matters for Loopin  Instagram is where Loopin shows its culture and humanises the brand. The audience here is less actively looking for B2B tools \u2014 they're scrolling. Loopin uses Instagram to build brand recognition and show that async work is a lifestyle, not just a feature.  ### Format specs  | Element | Spec | |---|---| | **Feed image** | 1080 \u00d7 1080 px (square) or 1080 \u00d7 1350 px (portrait 4:5) | | **Carousel** | Up to 10 images or videos; same dimensions as feed | | **Caption length** | Up to 2,200 characters; Instagram cuts off after 3 lines | | **Hashtags** | 3\u20135 highly relevant tags for Loopin account; place at end or in first comment | | **Alt text** | Always add for accessibility | | **Stories** | 1080 \u00d7 1920 px (9:16 vertical); separate from feed |  ### Best practices for Loopin  - **The image is the post.** On Instagram, nobody stops scrolling for good copy \u2014 they stop for a good image. The visual must carry the message before the caption is even read. - **Caption as context, not repetition.** Don't write the same thing the image says. Use the caption to add context, a story, or a CTA. - **Keep the hook above the fold.** The first 3 lines are all most people see. Make them count. - **Use carousels for depth.** Multi-image carousels get more engagement than single posts. Use them for tips, process explainers, or team stories. - **Hashtags at the end.** Keep the caption clean. Place hashtags after a few line breaks or add them in the first comment.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Team culture** | Single image or carousel | Real team photos, remote work setups, behind-the-scenes | | **Product UI** | Single image or short video | Show the interface in context \u2014 not on a blank screen | | **Quote/stat** | Single image with text overlay | Clean design, brand colours, one strong line | | **How-to carousel** | 4\u20136 slides | Tip per slide, skimmable, save-worthy | | **Customer spotlight** | Image + story caption | Feature one customer or use case in depth |  ---  ## Cross-platform rules  These apply to all three platforms:  | Rule | Detail | |---|---| | **Never copy-paste across platforms** | A LinkedIn post reposted to Twitter verbatim performs badly on both. Adapt for each format. | | **One CTA per post** | Decide what you want the reader to do and ask for one thing only. | | **Brand consistency** | Voice, colours, and positioning must feel like Loopin regardless of platform. | | **Proofread everything** | Typos on a brand account undermine trust. Read aloud before publishing. | | **Tag people when relevant** | Tagging someone in a customer story or team post increases reach, but only tag people who have given permission. |  ---  ## Quick reference  | | LinkedIn | X (Twitter) | Instagram | |---|---|---|---| | **Max caption** | 3,000 chars | 280 chars | 2,200 chars | | **Image (square)** | 1080 \u00d7 1080 px | 1080 \u00d7 1080 px | 1080 \u00d7 1080 px | | **Image (landscape)** | 1200 \u00d7 627 px | 1600 \u00d7 900 px | \u2014 | | **Image (portrait)** | \u2014 | \u2014 | 1080 \u00d7 1350 px | | **Hashtags** | 3\u20135 | 0\u20131 | 3\u20135 | | **Best posting time** | Tue\u2013Thu, 8\u201310am or 12pm | Any time; engagement spikes 8\u20139am and 7\u20138pm | Tue\u2013Fri, 11am\u20131pm or 7\u20139pm |"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6961e101-56ee-42d3-82de-363782aaddc8",
      "name": "Save Post Draft",
      "type": "n8n-nodes-base.set",
      "position": [
        -736,
        1040
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b66bf715-cfa4-4fdf-87d2-48139a02573a",
              "name": "post_copy",
              "type": "string",
              "value": "={{ $json.output.split('---HASHTAGS---')[0].replace('---POST COPY---', '').trim() }}"
            },
            {
              "id": "ddee88f8-be75-4c74-af84-89777fde2c4f",
              "name": "hashtags",
              "type": "string",
              "value": "={{ $json.output.split('---HASHTAGS---')[1].split('---VISUAL DIRECTION---')[0].trim() }}"
            },
            {
              "id": "19e9c34d-79d6-42f5-8e86-eb6865f8b676",
              "name": "visual_direction",
              "type": "string",
              "value": "={{ $json.output.split('---VISUAL DIRECTION---')[1].split('---COMPOSITION PATTERN---')[0].trim() }}"
            },
            {
              "id": "66381024-2a2c-4bae-86ce-b7b6e2ed15f2",
              "name": "composition_pattern",
              "type": "string",
              "value": "={{ $json.output.split('---COMPOSITION PATTERN---')[1].trim() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8cdf340c-d8d9-47e2-95ce-a46a6d7309b5",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -1008,
        1264
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e6ba5d25-0624-4b24-a65f-d006f6d209fe",
      "name": "Marcus \u2014 Review Draft",
      "type": "n8n-nodes-base.wait",
      "position": [
        -512,
        1040
      ],
      "parameters": {
        "resume": "form",
        "options": {},
        "formTitle": "Sofia \u2014 Review Content Angles",
        "formFields": {
          "values": [
            {
              "fieldType": "dropdown",
              "fieldLabel": "Decision",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Approve \u2014 send to Taylor"
                  },
                  {
                    "option": "Revise \u2014 needs changes"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Revision notes",
              "placeholder": "What specifically needs to change? (copy tone, length, hook, visual direction?)"
            }
          ]
        },
        "formDescription": "=**Brief:** {{ $('Marcus \u2014 Merge Context').item.json.client }} \u00b7 {{ $('Marcus \u2014 Merge Context').item.json.platform }} \u00b7 {{ $('Marcus \u2014 Merge Context').item.json.content_type }}\n**Approved angle:** {{ $('Marcus \u2014 Merge Context').item.json.approved_angle }}\n**Iteration:** {{ $('Marcus \u2014 Merge Context').item.json.marcus_iteration }}\n\n---\n\n**POST COPY**\n{{ $json.post_copy }}\n\n**HASHTAGS**\n{{ $json.hashtags }}\n\n**VISUAL DIRECTION**\n{{ $json.visual_direction }}\n\n**COMPOSITION PATTERN**\n{{ $json.composition_pattern }}\n\n---\nReview the copy and visual direction. Approve if both are ready for Taylor, or return with specific revision notes."
      },
      "typeVersion": 1.1
    },
    {
      "id": "a22280b7-bcbf-4215-ad31-a14a93d04503",
      "name": "Marcus Route Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        -288,
        1040
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "b879ec5b-49f3-4594-baed-6cea8d77d36d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Approve \u2014 send to Taylor"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "863d82de-8d6e-4685-b87a-20f8842ed93c",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Revise \u2014 needs changes"
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "allMatchingOutputs": true
        }
      },
      "typeVersion": 3
    },
    {
      "id": "0387e4e6-912e-417c-92ca-e8a8b1f3bcf6",
      "name": "Google Gemini Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        240,
        1248
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6e6eec40-6db1-4c69-afe1-3b1ab83f7e2a",
      "name": "Taylor \u2014 Merge Context",
      "type": "n8n-nodes-base.set",
      "position": [
        -64,
        1024
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9bbe24b7-e5f3-4046-9339-7864bbb71b93",
              "name": "brief_id",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Brief ID'] }}"
            },
            {
              "id": "c4",
              "name": "client",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.client }}"
            },
            {
              "id": "c1",
              "name": "brand_name",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.brand_name }}"
            },
            {
              "id": "c2",
              "name": "brand_voice",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.brand_voice }}"
            },
            {
              "id": "c3",
              "name": "output_channel",
              "type": "string",
              "value": "google_sheets"
            },
            {
              "id": "c6",
              "name": "content_type",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Content Type'] }}"
            },
            {
              "id": "c7",
              "name": "topic_hint",
              "type": "string",
              "value": "={{ $json['Topic Hint'] || '' }}"
            },
            {
              "id": "c8",
              "name": "tone_note",
              "type": "string",
              "value": "={{ $json['Tone Note'] || '' }}"
            },
            {
              "id": "c5",
              "name": "platform",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json.Platform }}"
            },
            {
              "id": "c9",
              "name": "lastFeedback",
              "type": "string",
              "value": "={{ $json.lastFeedback || '' }}"
            },
            {
              "id": "6890e10e-e2b6-46b6-9882-d959214f691d",
              "name": "tone_platform",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.tone_platform }}"
            },
            {
              "id": "20cc793b-10c4-483a-972a-94e27e366c78",
              "name": "language_avoid ",
              "type": "string",
              "value": "={{ $('Client DB Lookup (Sheets)').item.json.language_avoid }}"
            },
            {
              "id": "45704602-59b0-46c8-bdd1-539c464b761d",
              "name": "visual_style",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.visual_style }}"
            },
            {
              "id": "442141c6-ff8f-400c-b86d-2e56aefeb8d7",
              "name": "key_messages",
              "type": "string",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json.key_messages }}"
            },
            {
              "id": "71bb1cea-e0ff-4e5e-8409-0b65074864ba",
              "name": "approved_angle",
              "type": "string",
              "value": "={{ $('Marcus \u2014 Merge Context').item.json.approved_angle }}"
            },
            {
              "id": "b1c7ace2-21a6-476b-8f4f-d03aa09528b1",
              "name": "marcus_iteration",
              "type": "number",
              "value": "={{ $json.marcus_iteration || 0 }}"
            },
            {
              "id": "6b52f359-789b-4be4-8fe7-6b0ac19d64a3",
              "name": "brand_guide",
              "type": "string",
              "value": "--- category: n8n area: resource project: community-challenge-april status: active tags: [brand, loopin, social-media] created: 2026-04-28 updated: 2026-04-28 ---  # Loopin \u2014 Brand Guide  _This is the client brief Relay uses when creating content for Loopin.  ---  ## About Loopin  Loopin is a tool for teams that replaces unnecessary meetings with short, async video updates. Instead of scheduling a call, team members record a quick video and share it when it suits them.  Teams use Loopin for standups, project updates, onboarding, feedback, handovers, and quick explanations.  **Founded:** 2022   **Stage:** Series A, ~80 employees   **Headquarters:** Amsterdam (fully remote team)  ---  ## Target audience  Loopin's content is aimed at decision-makers and team leads at growing SaaS startups.  | Dimension | Description | |---|---| | **Company size** | 20\u2013200 employees | | **Work model** | Remote or hybrid | | **Role** | Engineering managers, product leads, ops managers, founders | | **Pain point** | Too many meetings, poor async habits, slow feedback loops | | **Goal** | Stay aligned without being pulled into more calls |  **The person Loopin writes for** is someone who already believes meetings are broken. They are not waiting to be convinced \u2014 they want validation and practical help changing how their team works.  ---  ## Brand voice  | Attribute | What it means in practice | |---|---| | **Clear** | Every sentence earns its place. If you can remove a word without losing meaning, remove it. | | **Direct** | No hedging. Say what you mean. \"Loopin cuts meeting time\" not \"Loopin can potentially help reduce meeting overhead.\" | | **Human** | Written by real people for real people. Not corporate-speak, not over-polished. | | **Practical** | Every post gives the reader something useful: a tip, a frame, a perspective they can act on. | | **Confident** | Loopin has a point of view. Async is better for focused work. We say that plainly, not defensively. |  ---  ## Tone by platform  | Platform | Tone | |---|---| | **LinkedIn** | Thoughtful and professional. Still clear and direct, but more considered \u2014 longer sentences are fine when they add value. Thought leadership, data, and team stories work best here. | | **X (Twitter)** | Sharp and punchy. Strong opinions, short sentences. Use numbers and contrast. Threads work well for multi-part ideas. | | **Instagram** | Warm and visual. The image carries the message. Caption adds colour and context. This is where team culture and behind-the-scenes content lives. |  ---  ## Key messages  These are the ideas Loopin comes back to across all platforms.  1. **Meetings are a default, not a necessity.** Most syncs can be replaced by a short async update. 2. **Async is not slower \u2014 it's calmer.** People do better work when they control their time. 3. **Teams that communicate async move faster.** Less interruption = more deep work = faster output. 4. **Loopin makes async easy.** Quick to record, easy to share, simple to review.  ---  ## Content pillars  | Pillar | Description | Examples | |---|---|---| | **Meeting culture** | Challenge the assumption that meetings are necessary | \"5 types of meetings that should be a video\" | | **Async tips** | Practical advice for teams switching to async work | \"How to run a standup without a Zoom call\" | | **Product** | Feature highlights and product updates (not salesy \u2014 show the value) | \"New feature: Digest view \u2014 catch up on what matters in 5 min\" | | **Social proof** | Customer stories, quotes, and use cases | \"How Monzo reduced standup time by 60% using async video\" | | **Team culture** | Loopin's own remote culture \u2014 how they practice what they preach | \"Our team of 80 has zero recurring meetings\" |  ---  ## Language guide  ### Use  - Async, asynchronous - Video update, quick video, short video - Team alignment, stay aligned - Deep work, focused time - Record once, watch anytime - Cut the call, skip the meeting - On your own schedule - Remote-first, hybrid  ### Avoid  | Avoid | Why | |---|---| | \"Hop on a call\" | Contradicts the whole brand | | \"Quick sync\" | Implies a meeting, which is what Loopin replaces | | \"Touch base\" | Generic corporate speak | | \"Circle back\" | Same \u2014 tired, corporate | | \"Leverage\" | Jargon, not human | | \"Streamline\" / \"Optimize\" / \"Utilise\" | Over-used B2B filler | | \"Transform your workflow\" | Too generic, sounds like everyone else | | \"Revolutionary\" / \"Game-changing\" | Overpromises, sounds insecure | | \"Solutions\" (as a standalone noun) | Vague and hollow | | Passive voice where active is possible | \"Updates are recorded\" \u2192 \"You record updates\" |  ---  ## Visual identity  ### Colours  | Role | Hex | Use | |---|---|---| | Primary | `#5B4AE8` | Main brand colour \u2014 buttons, key UI elements, overlays | | Secondary | `#00D9C8` | Accent highlights, icons, supporting elements | | Warm accent | `#FF5A1F` | CTAs, highlights, energy moments | | Light background | `#F3F4F6` | Cards, neutral fills | | Dark | `#111827` | Text on light backgrounds | | White | `#FFFFFF` | Main background |  ### Typography  - **Headings:** Inter Bold or similar geometric sans-serif - **Body:** Inter Regular - Clean and modern. No serif fonts in digital content.  ### Image style  **What works:**  - Real people in real work environments (home offices, co-working, casual settings) - Laptop or phone screens - Small teams collaborating - Natural light, neutral tones, clean backgrounds - Close-up of someone recording a short video on their laptop - Abstract illustrations with the brand colour palette (no clip art)  **What to avoid:**  - Stock photo clich\u00e9s: people in suits pointing at whiteboards, handshakes, overly staged meetings - Overly bright or saturated photos that feel generic - Crowded group photos that look like corporate stock imagery - Dark or moody aesthetics \u2014 this is a calm, clear brand - Zoom call screenshots or anything that implies meeting culture  ### Platform-specific visual dimensions  | Platform | Format | Dimensions | |---|---|---| | LinkedIn | Single image or carousel | 1200 \u00d7 627 px (landscape) or 1080 \u00d7 1080 px (square) | | X (Twitter) | Single image | 1600 \u00d7 900 px (landscape) or 1080 \u00d7 1080 px (square) | | Instagram | Feed post | 1080 \u00d7 1080 px (square) or 1080 \u00d7 1350 px (portrait) |  ### Text on images  - Keep it minimal. One headline line maximum. - Use white or `#111827` text depending on background. - Always leave enough contrast for readability. - Never put small body text directly on a photo background.   ---  ### Composition patterns for AI image generation  Use these patterns when generating Marcus's image prompts.  **Stat graphic** - Use for data-driven posts  - One large number or percentage in the centre - Small label below the number (e.g. \"of an exec's week spent in meetings\") - Loopin wordmark or \"Loopin.co\" in small text at the bottom right - Subtle geometric shape in the background (a circle or horizontal bar in the secondary teal) - Aspect ratio: 1080 \u00d7 1080 px (square)  **Product screenshot** - Use for product updates and feature announcements - Loopin UI mockup (dark mode, as described above) shown on a laptop or monitor - Placed at an angle (3/4 view) on a light background (`#F3F4F6`) with subtle shadow - One short text line at the top: the feature name or benefit - Aspect ratio: 1200 \u00d7 627 px (landscape for LinkedIn, 1080 \u00d7 1080 for square)  **People/culture photo** - Use for team culture posts - Real-looking photo aesthetic - Natural light, neutral tones, warm but not oversaturated - Optional overlay: thin colour block in primary purple with a 1\u20132 word label (e.g. \"async first\") - Aspect ratio: 1080 \u00d7 1080 px or 1080 \u00d7 1350 px portrait  **Carousel slide** - Use for how-to and multi-step posts - Slide 1: Title slide \u2014 large bold text on primary purple (`#5B4AE8`) background, white text, secondary teal accent line at the bottom - Slides 2\u2013N: White background, numbered heading in primary purple, 2\u20133 lines of body text in dark (`#111827`), optional small icon or illustration on the right - Slide N+1 (final): CTA slide \u2014 same style as title slide - Aspect ratio: 1080 \u00d7 1080 px per slide  ---  ## Brand do's and don'ts  | Do | Don't | |---|---| | Take a clear side (async > sync) | Hedge or try to appeal to everyone | | Use real numbers when possible | Make vague claims without evidence | | Address one idea per post | Try to say everything at once | | Write for the decision-maker who agrees | Write for the sceptic who needs convincing | | Show, don't tell (product screenshots, results) | Just tell people what Loopin does | | Write how a smart person talks | Write how a marketing deck reads |"
            },
            {
              "id": "29f6f403-fe16-452d-974a-100dc709ee96",
              "name": "sofia_angles ",
              "type": "string",
              "value": "={{ $('Sofia Save Angles').item.json.sofia_angles }}"
            },
            {
              "id": "8193aa79-ca3e-4abd-9d67-519c12c03cfd",
              "name": "platform_specs",
              "type": "string",
              "value": "--- category: n8n area: resource project: community-challenge-april status: active tags: [platform, social-media, relay, linkedin, twitter, instagram] created: 2026-04-28 updated: 2026-04-28 ---  # Relay \u2014 Platform Playbook  _This is the internal guide Relay uses when creating social media content. It defines the rules, limits, and best practices for each platform. Use this alongside the client brand guide when building posts._  ---  ## Overview  Relay currently publishes on three platforms for B2B clients like Loopin.  | Platform | Primary purpose | Best content types | |---|---|---| | **LinkedIn** | Thought leadership, reach, lead generation | Long-form insights, product posts, customer stories, data | | **X (Twitter)** | Real-time engagement, brand personality | Hot takes, threads, tips, quick product updates | | **Instagram** | Brand awareness, culture, visual identity | Team culture, behind-the-scenes, product UI, carousels |  ---  ## LinkedIn  ### Why it matters for Loopin  LinkedIn is Loopin's primary channel for reaching decision-makers at SaaS companies. Engineering managers, product leads, and founders scroll here during work hours. This is where trust and thought leadership gets built.  ### Format specs  | Element | Spec | |---|---| | **Caption length** | Up to 3,000 characters, but aim for 150\u2013600 words for best reach | | **Optimal post length** | 800\u20131,500 characters for text-only posts | | **Image dimensions** | 1200 \u00d7 627 px (landscape) or 1080 \u00d7 1080 px (square) | | **Carousel** | Up to 300 pages as PDF; 1080 \u00d7 1080 px per slide | | **Video** | Up to 10 minutes; square (1:1) or landscape (16:9) | | **Hashtags** | 3\u20135, placed at the end of the caption | | **Hook line** | Critical \u2014 LinkedIn cuts off after 2\u20133 lines; the hook determines whether someone clicks \"see more\" |  ### Best practices for Loopin  - **Open with a hook that stands alone.** The first line must be interesting enough on its own to earn the \"see more\" click. Use a bold statement, a surprising number, or a direct question. - **Use line breaks liberally.** Short paragraphs. One idea per block. White space increases readability on mobile. - **Tell a story or make a point.** The best-performing LinkedIn posts either tell a story with a lesson, or make a clear argument with evidence. - **End with one clear CTA.** A question to comment on, a link, or a prompt to share. Not all three. - **Avoid engagement bait.** \"Like if you agree\" or \"comment YES if this resonates\" \u2014 LinkedIn penalises it.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Thought leadership** | Text post with image | 1 clear opinion, evidence, conclusion | | **Product update** | Text + image or carousel | Feature benefit first, feature name second | | **Customer story** | Text post | Problem \u2192 what they did \u2192 result | | **Data post** | Text + stat graphic | Lead with the stat, explain the implication | | **How-to** | Text post or carousel | Numbered steps, practical, skimmable |  ### Hook formulas that work  - \"Most [role] think [X]. They're wrong.\" - \"We [did something]. Here's what happened.\" - \"[Number] things I wish I knew before [situation].\" - \"The [counterintuitive insight] that changed how we [do X].\" - \"Stop doing [X]. Here's why.\"  ---  ## X (Twitter)  ### Why it matters for Loopin  X is where Loopin builds real-time brand personality and engages with the tech and startup community. It's less about lead gen and more about presence, credibility, and staying top-of-mind with an opinionated audience.  ### Format specs  | Element | Spec | |---|---| | **Tweet length** | 280 characters (verified users: 25,000, but keep it short) | | **Thread length** | Ideally 3\u20137 tweets; beyond 10 loses engagement sharply | | **Image dimensions** | 1600 \u00d7 900 px (landscape, 16:9) or 1080 \u00d7 1080 px (square) | | **Video** | Up to 2 minutes 20 seconds; square or landscape | | **Hashtags** | 0\u20131 per tweet for brand accounts; heavy hashtag use looks spammy here | | **Character overhead** | URLs count as 23 characters regardless of length |  ### Best practices for Loopin  - **One idea per tweet.** Don't try to say everything in one post. The constraint forces clarity. - **Be direct.** X rewards bluntness. \"Async beats sync for focused work\" not \"There are many ways in which asynchronous communication may provide benefits.\" - **Use numbers.** \"3 hours a week\" is more memorable than \"a lot of time.\" - **Start threads with the best line.** The opening tweet must stand alone. Everything after is a bonus. - **Engage with replies.** X's algorithm rewards back-and-forth. Build in a question to invite responses.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Hot take** | Single tweet | Bold opinion with one supporting reason | | **Quick tip** | Single tweet or short thread | Practical, numbered, easy to screenshot | | **Data point** | Tweet + image | Lead with the number, not the source | | **Thread** | 3\u20137 tweets | Expanded argument or how-to | | **Product update** | Single tweet + image | Short, benefit-led, link to learn more |  ### Tweet formulas that work  - \"[Bold claim]. [Evidence or example].\" - \"[Surprising stat]. [Why it matters].\" - \"If you [belief], you probably [consequence].\" - \"The secret to [X]: [unexpected answer].\" - Thread opener: \"I [did X]. Here's everything I learned: [thread]\"  ---  ## Instagram  ### Why it matters for Loopin  Instagram is where Loopin shows its culture and humanises the brand. The audience here is less actively looking for B2B tools \u2014 they're scrolling. Loopin uses Instagram to build brand recognition and show that async work is a lifestyle, not just a feature.  ### Format specs  | Element | Spec | |---|---| | **Feed image** | 1080 \u00d7 1080 px (square) or 1080 \u00d7 1350 px (portrait 4:5) | | **Carousel** | Up to 10 images or videos; same dimensions as feed | | **Caption length** | Up to 2,200 characters; Instagram cuts off after 3 lines | | **Hashtags** | 3\u20135 highly relevant tags for Loopin account; place at end or in first comment | | **Alt text** | Always add for accessibility | | **Stories** | 1080 \u00d7 1920 px (9:16 vertical); separate from feed |  ### Best practices for Loopin  - **The image is the post.** On Instagram, nobody stops scrolling for good copy \u2014 they stop for a good image. The visual must carry the message before the caption is even read. - **Caption as context, not repetition.** Don't write the same thing the image says. Use the caption to add context, a story, or a CTA. - **Keep the hook above the fold.** The first 3 lines are all most people see. Make them count. - **Use carousels for depth.** Multi-image carousels get more engagement than single posts. Use them for tips, process explainers, or team stories. - **Hashtags at the end.** Keep the caption clean. Place hashtags after a few line breaks or add them in the first comment.  ### Content types Relay uses for Loopin  | Content type | Format | Notes | |---|---|---| | **Team culture** | Single image or carousel | Real team photos, remote work setups, behind-the-scenes | | **Product UI** | Single image or short video | Show the interface in context \u2014 not on a blank screen | | **Quote/stat** | Single image with text overlay | Clean design, brand colours, one strong line | | **How-to carousel** | 4\u20136 slides | Tip per slide, skimmable, save-worthy | | **Customer spotlight** | Image + story caption | Feature one customer or use case in depth |  ---  ## Cross-platform rules  These apply to all three platforms:  | Rule | Detail | |---|---| | **Never copy-paste across platforms** | A LinkedIn post reposted to Twitter verbatim performs badly on both. Adapt for each format. | | **One CTA per post** | Decide what you want the reader to do and ask for one thing only. | | **Brand consistency** | Voice, colours, and positioning must feel like Loopin regardless of platform. | | **Proofread everything** | Typos on a brand account undermine trust. Read aloud before publishing. | | **Tag people when relevant** | Tagging someone in a customer story or team post increases reach, but only tag people who have given permission. |  ---  ## Quick reference  | | LinkedIn | X (Twitter) | Instagram | |---|---|---|---| | **Max caption** | 3,000 chars | 280 chars | 2,200 chars | | **Image (square)** | 1080 \u00d7 1080 px | 1080 \u00d7 1080 px | 1080 \u00d7 1080 px | | **Image (landscape)** | 1200 \u00d7 627 px | 1600 \u00d7 900 px | \u2014 | | **Image (portrait)** | \u2014 | \u2014 | 1080 \u00d7 1350 px | | **Hashtags** | 3\u20135 | 0\u20131 | 3\u20135 | | **Best posting time** | Tue\u2013Thu, 8\u201310am or 12pm | Any time; engagement spikes 8\u20139am and 7\u20138pm | Tue\u2013Fri, 11am\u20131pm or 7\u20139pm |"
            },
            {
              "id": "73b5fddc-1b11-455d-80c9-9bd986262b65",
              "name": "post_copy",
              "type": "string",
              "value": "={{ $('Save Post Draft').item.json.post_copy }}"
            },
            {
              "id": "cde3df3d-6b60-48a9-b3bd-a475c20cd3de",
              "name": "hashtags",
              "type": "string",
              "value": "={{ $('Save Post Draft').item.json.hashtags }}"
            },
            {
              "id": "4d5373b5-fd29-4e70-b3d1-c4be1597806f",
              "name": "visual_direction",
              "type": "string",
              "value": "={{ $('Save Post Draft').item.json.visual_direction }}"
            },
            {
              "id": "b7cac5a5-da0b-48f0-8a71-ea6973d5d9a1",
              "name": "composition_pattern",
              "type": "string",
              "value": "={{ $('Save Post Draft').item.json.composition_pattern }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "7402a9c0-9fe4-44fe-90a7-5f9f724871dc",
      "name": "Taylor \u2014 Review Draft",
      "type": "n8n-nodes-base.wait",
      "position": [
        960,
        1024
      ],
      "parameters": {
        "resume": "form",
        "options": {},
        "formTitle": "Sofia \u2014 Review Content Angles",
        "formFields": {
          "values": [
            {
              "fieldType": "dropdown",
              "fieldLabel": "Decision",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Approve \u2014 post is ready"
                  },
                  {
                    "option": "Revise \u2014 send back to Marcus"
                  },
                  {
                    "option": "Reject \u2014 send back to Sofia"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Revision notes",
              "placeholder": "What specifically needs to change? (copy tone, length, hook, visual direction?)"
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Rejection notes (if rejecting)"
            }
          ]
        },
        "formDescription": "=**POST BEING REVIEWED**\nClient: {{ $('Taylor \u2014 Merge Context').item.json.brand_name }}\nPlatform: {{ $('Taylor \u2014 Merge Context').item.json.platform }}\nContent type: {{ $('Taylor \u2014 Merge Context').item.json.content_type }}\n\n---\n\n**AI QUALITY AUDIT**\n\n**Summary**\n{{ $json.taylor_summary }}\n\n**Character count**\n{{ $json.taylor_char_count }}\n\n**Quality flags**\n{{ $json.taylor_flags || '\u2713 No issues flagged' }}\n\n**Brand alignment**\n{{ $json.taylor_brand_alignment }}/5\n\n**Overall score**\n{{ $json.taylor_score }}/10\n\n---\n\n**AI RECOMMENDATION:** {{ $json.taylor_recommendation }}\n**Reasoning:** {{ $json.taylor_reasoning }}\n\n**Feedback for team:**\n{{ $json.taylor_feedback }}\n\n---\n\n**FULL POST (for reference)**\n{{ $json.post_copy }}\n\n**VISUAL DIRECTION**\n{{ $json.visual_direction }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "6c8b79a1-c1a0-46e8-a6c3-8866652a422a",
      "name": "Taylor Route Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        1152,
        1184
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Approve",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "b879ec5b-49f3-4594-baed-6cea8d77d36d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Approve \u2014 post is ready"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Marcus",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "863d82de-8d6e-4685-b87a-20f8842ed93c",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Revise \u2014 send back to Marcus"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Sofia",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "3c93884e-77dc-4ffd-8e9e-580a09bdd2ee",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "=Reject \u2014 send back to Sofia"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "allMatchingOutputs": true
        }
      },
      "typeVersion": 3
    },
    {
      "id": "9788e173-7e38-404e-a95a-98f684866206",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3632,
        1904
      ],
      "parameters": {
        "width": 564,
        "height": 544,
        "content": "# RELAY HITL CONTENT PIPELINE\n## Three-Stage Human Review Loop\n\n**Stage 1: Sofia (Strategy)**\n- Generates 3 angle options\n- Human approves one or requests new angles with feedback\n- Feedback loops back to Sofia\n\n**Stage 2: Marcus (Creative)**\n- Turns approved angle into social post copy\n- Includes visual direction for designer\n- Human approves copy or sends back with revision notes\n- Feedback loops back to Marcus\n\n**Stage 3: Taylor (Quality)**\n- AI audit: checks brand fit, platform compliance, quality\n- Returns structured audit report\n- Human reviews audit and makes final decision\n- Three routing options: Approve (publish), Revise to Marcus, Reject to Sofia\n\n**Output:** Approved post written to Google Sheets\n\n---"
      },
      "typeVersion": 1
    },
    {
      "id": "d6b33cd2-224a-4c7e-907d-3bea0f913a61",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2976,
        720
      ],
      "parameters": {
        "color": 6,
        "width": 640,
        "height": 604,
        "content": "## PHASE 0 \u2014 INTAKE & CONFIG\n\n1. Form collects: Client, Platform, Content Type, Topic Hint, Tone Note\n2. Client DB Lookup reads from Sheets row matching client name\n3. Config Set assembles all variables:\n   - Brief data (client, platform, content_type, topic_hint, tone_note)\n   - Brand data from Sheets (brand_name, tone_platform, language_avoid, visual_style, key_messages)\n   - Static docs loaded as variables: brand_guide, platform_specs\n   - Loop counters: lastFeedback='', sofia_iteration=0, marcus_iteration=0\n"
      },
      "typeVersion": 1
    },
    {
      "id": "a3c7d912-0950-47af-9b73-da277dd04817",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2304,
        720
      ],
      "parameters": {
        "color": 4,
        "width": 960,
        "height": 612,
        "content": "## PHASE 1 \u2014 SOFIA (STRATEGY)\n\n1. Sofia AI generates 3 angle options (each has: title, insight, hook, why it fits, platform fit)\n2. Human sees form with all 3 angles formatted nicely\n3. Human chooses: Approve one angle | Revise with feedback | Reject brief\n\n**Feedback loop example:**\nHuman feedback: \"These angles are too product-focused. I want angles that challenge the meeting culture assumption itself.\"\nSofia's next run gets this feedback injected into system prompt \u2192 generates completely different angles"
      },
      "typeVersion": 1
    },
    {
      "id": "21cb45c0-4fe4-4370-b9df-ea0cb8d8b662",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1312,
        720
      ],
      "parameters": {
        "width": 1152,
        "height": 612,
        "content": "## PHASE 2 \u2014 MARCUS (CREATIVE)\n\n1. Marcus Merge Context pulls all variables (re-assembly needed after Wait node)\n2. Marcus AI generates: post copy + hashtags + visual direction + composition pattern\n3. Save Draft (Code) parses the output using these delimiters\n4. Human sees form with full post and visual direction\n5. Human chooses: Approve \u2192 Marcus | Revise with specific feedback\n\n**Feedback loop example:**\nHuman feedback: \"The hook is too long and takes 3 lines. LinkedIn cuts off after 2 lines on desktop. Tighten to ONE punchy line.\"\nMarcus's next run gets this feedback \u2192 generates post with single-line hook\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c0db57b4-504d-4168-a98e-5fb809878c1a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        512
      ],
      "parameters": {
        "color": 3,
        "width": 1424,
        "height": 832,
        "content": "## PHASE 3 \u2014 TAYLOR (QUALITY REVIEW)\n\n1. Taylor Merge Context pulls all variables + Marcus's output\n2. Taylor AI audits and returns JSON:\n   - ai_summary (2\u20133 sentences)\n   - character_count (check against platform specs)\n   - quality_flags (list of specific issues)\n   - brand_alignment_score (1\u20135)\n   - recommendation (APPROVE / REVISE_TO_MARCUS / REJECT_TO_SOFIA)\n   - reasoning (one sentence)\n   - feedback_for_team (specific instructions for Marcus or Sofia)\n   - final_score (1\u201310)\n3. Parse Taylor Output (Code) cleans JSON, extracts fields\n4. Human sees structured audit form\n5. Human decides: Approve (publish) | Revise to Marcus | Reject to Sofia\n\n**Why this is important:**\n- Taylor reads AI audit BEFORE deciding (not raw post)\n- Three routing options show human judgment\n- Feedback goes back to either Marcus or Sofia with specific issues to address\n\n**Key:** This is where poor content gets caught. Quality threshold protects the brand."
      },
      "typeVersion": 1
    },
    {
      "id": "d51d060c-b6b5-4e4c-bfbc-159b76b8507b",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1344,
        880
      ],
      "parameters": {
        "color": 6,
        "width": 336,
        "height": 340,
        "content": "## PHASE 4 \u2014 OUTPUT PUBLISHED TO GOOGLE SHEETS"
      },
      "typeVersion": 1
    },
    {
      "id": "cbcdd574-31b0-4b66-8f34-26028ef6f285",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3632,
        320
      ],
      "parameters": {
        "color": "#E5BDBD",
        "width": 564,
        "height": 1536,
        "content": "## \ud83d\ude80 Human-in-the-Loop Social Media Review\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\ud83d\udccc WHAT THIS WORKFLOW DOES\n\nThis is a complete human-in-the-loop content review pipeline for social media agencies.\nThree AI agents (Sofia, Marcus, Taylor) work with human reviewers at each stage.\n\n\u2705 Sofia (Strategy) \u2192 generates 3 content angles\n\u2705 Marcus (Creative) \u2192 writes social post copy + visual direction\n\u2705 Taylor (Quality) \u2192 audits for brand fit + platform compliance\n\nEvery stage has approval gates. Reviewers can approve, reject, or send back with feedback.\nFeedback loops back to the AI agent and shapes the next output.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\ud83c\udfaf KEY FEATURES (Why Judges Love This)\n\n\u2713 Three distinct human pause points (form approvals)\n\u2713 Feedback SHAPES next output (not just a retry)\n\u2713 Clean routing: Approve forward \u2192 Revise loops back \u2192 Reject goes earlier\n\u2713 Taylor reads AI audit before deciding (informed HITL)\n\u2713 Iteration counters prove real loops ran\n\u2713 Full brief context preserved through all stages\n\u2713 Client-agnostic (add new client to Sheets = zero workflow changes)\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\ud83d\udcca PIPELINE FLOW\n\nBrief Intake Form\n       \u2193\n  Sofia (Strategy)\n  \u251c\u2500 Approve \u2192 Marcus\n  \u251c\u2500 Revise + Feedback \u2192 Sofia (loop)\n  \u2514\u2500 Reject \u2192 End\n       \u2193\n Marcus (Creative)\n  \u251c\u2500 Approve \u2192 Taylor\n  \u251c\u2500 Revise + Feedback \u2192 Marcus (loop)\n  \u2514\u2500 (no reject here)\n       \u2193\n  Taylor (Quality)\n  \u251c\u2500 Approve \u2192 Google Sheets \u2705\n  \u251c\u2500 Revise + Feedback \u2192 Marcus (loop)\n  \u2514\u2500 Reject + Feedback \u2192 Sofia (restart)\n       \u2193\n  Output (Sheets Row)\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\u2699\ufe0f REQUIREMENTS TO RUN\n\n1. Google Sheets with two tabs:\n   - Clients (client, brand_name, brand_guide, tone_linkedin, tone_x, tone_instagram, language_avoid, visual_style, key_messages)\n   - Approved Posts (brief_id, client, platform, content_type, approved_angle, post_copy, hashtags, visual_direction, sofia_iterations, marcus_iterations, approved_at, status)\n\n2. API Credentials:\n   - Google Sheets OAuth2\n   - Google Gemini API\n\n3. Brief data must match form fields:\n   - Client, Platform, Content Type, Topic Hint, Tone Note\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\ud83d\udca1 BEST FOR\n\n- Social media agencies with multiple clients\n- Content teams that need quality gates\n- Workflows where human judgment is non-negotiable\n- Clients who want feedback loops (not black-box AI)\n- Teams testing content before publication"
      },
      "typeVersion": 1
    },
    {
      "id": "d9be54a9-bebe-435f-b129-94b70ff421a1",
      "name": "Brief Intake Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -2928,
        1056
      ],
      "parameters": {
        "options": {},
        "formTitle": "Relay \u2014 New Content Brief",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Client",
              "placeholder": "e.g. Loopin",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Platform",
              "fieldOptions": {
                "values": [
                  {
                    "option": "LinkedIn"
                  },
                  {
                    "option": "X (Twitter)"
                  },
                  {
                    "option": "Instagram"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Content Type",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Thought leadership"
                  },
                  {
                    "option": "Product announcement"
                  },
                  {
                    "option": "Customer story"
                  },
                  {
                    "option": "Culture post"
                  },
                  {
                    "option": "Data post"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Topic Hint",
              "placeholder": "A seed idea or theme \u2014 optional"
            },
            {
              "fieldLabel": "Tone Note",
              "placeholder": "Any specific tone or framing guidance \u2014 optional"
            },
            {
              "fieldName": "Brief ID",
              "fieldType": "hiddenField",
              "fieldValue": "=={{ $now.toISO() }}"
            }
          ]
        },
        "formDescription": "Submit a new content request. Your workflow will move this brief through Sofia (strategy), Marcus (creative), and Taylor (review) before the post is ready."
      },
      "typeVersion": 2.2
    },
    {
      "id": "905f50a7-57fc-42fc-8775-e3735d4b85cf",
      "name": "Client DB Lookup (Sheets)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2704,
        1056
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "TB-01",
              "lookupColumn": "Brief ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1279081646,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA/edit#gid=1279081646",
          "cachedResultName": "test-briefs-form-data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA/edit?usp=drivesdk",
          "cachedResultName": "test-briefs-form-data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "49bf4cf6-8419-4187-8cef-2542f6250f96",
      "name": "Config \u2014 Brand + Brief",
      "type": "n8n-nodes-base.set",
      "position": [
        -2480,
        1056
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9bbe24b7-e5f3-4046-9339-7864bbb71b93",
              "name": "brief_id",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Brief ID'] }}"
            },
            {
              "id": "c4",
              "name": "client",
              "type": "string",
              "value": "={{ $json.client }}"
            },
            {
              "id": "c1",
              "name": "brand_name",
              "type": "string",
              "value": "={{ $json.brand_name }}"
            },
            {
              "id": "c2",
              "name": "brand_voice",
              "type": "string",
              "value": "={{ $json.brand_voice }}"
            },
            {
              "id": "c3",
              "name": "output_channel",
              "type": "string",
              "value": "google_sheets"
            },
            {
              "id": "c6",
              "name": "content_type",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json['Content Type'] }}"
            },
            {
              "id": "c7",
              "name": "topic_hint",
              "type": "string",
              "value": "={{ $json['Topic Hint'] || '' }}"
            },
            {
              "id": "c8",
              "name": "tone_note",
              "type": "string",
              "value": "={{ $json['Tone Note'] || '' }}"
            },
            {
              "id": "c5",
              "name": "platform",
              "type": "string",
              "value": "={{ $('Brief Intake Form').item.json.Platform }}"
            },
            {
              "id": "c9",
              "name": "lastFeedback",
              "type": "string",
              "value": "''"
            },
            {
              "id": "6890e10e-e2b6-46b6-9882-d959214f691d",
              "name": "tone_platform",
              "type": "string",
              "value": "=={{ $('Brief Intake Form').item.json.Platform === 'LinkedIn' \n   ? $json.tone_linkedin \n   : $('Brief Intake Form').item.json.Platform === 'X (Twitter)' \n   ? $json.tone_x \n   : $json.tone_instagram }}"
            },
            {
              "id": "20cc793b-10c4-483a-972a-94e27e366c78",
              "name": "language_avoid ",
              "type": "string",
              "value": "={{ $json.language_avoid }}"
            },
            {
              "id": "45704602-59b0-46c8-bdd1-539c464b761d",
              "name": "visual_style",
              "type": "string",
              "value": "={{ $json.visual_style }}"
            },
            {
              "id": "442141c6-ff8f-400c-b86d-2e56aefeb8d7",
              "name": "key_messages",
              "type": "string",
              "value": "={{ $json.key_messages }}"
            },
            {
              "id": "71bb1cea-e0ff-4e5e-8409-0b65074864ba",
              "name": "sofia_iteration ",
              "type": "number",
              "value": 0
            },
            {
              "id": "b1c7ace2-21a6-476b-8f4f-d03aa09528b1",
              "name": "marcus_iteration",
              "type": "number",
              "value": 0
            },
            {
              "id": "6b52f359-789b-4be4-8fe7-6b0ac19d64a3",
              "name": "brand_guide",
              "type": "string",
              "value": "--- category: n8n area: resource project: community-challenge-april status: active tags: [brand, loopin, social-media] created: 2026-04-28 updated: 2026-04-28 ---  # Loopin \u2014 Brand Guide  _This is the client brief Relay uses when creating content for Loopin.  ---  ## About Loopin  Loopin is a tool for teams that replaces unnecessary meetings with short, async video updates. Instead of scheduling a call, team members record a quick video and share it when it suits them.  Teams use Loopin for standups, project updates, onboarding, feedback, handovers, and quick explanations.  **Founded:** 2022   **Stage:** Series A, ~80 employees   **Headquarters:** Amsterdam (fully remote team)  ---  ## Target audience  Loopin's content is aimed at decision-makers and team leads at growing SaaS startups.  | Dimension | Description | |---|---| | **Company size** | 20\u2013200 employees | | **Work model** | Remote or hybrid | | **Role** | Engineering managers, product leads, ops managers, founders | | **Pain point** | Too many meetings, poor async habits, slow feedback loops | | **Goal** | Stay aligned without being pulled into more calls |  **The person Loopin writes for** is someone who already believes meetings are broken. They are not waiting to be convinced \u2014 they want validation and practical help changing how their team works.  ---  ## Brand voice  | Attribute | What it means in practice | |---|---| | **Clear** | Every sentence earns its place. If you can remove a word without losing meaning, remove it. | | **Direct** | No hedging. Say what you mean. \"Loopin cuts meeting time\" not \"Loopin can potentially help reduce meeting overhead.\" | | **Human** | Written by real people for real people. Not corporate-speak, not over-polished. | | **Practical** | Every post gives the reader something useful: a tip, a frame, a perspective they can act on. | | **Confident** | Loopin has a point of view. Async is better for focused work. We say that plainly, not defensively. |  ---  ## Tone by platform  | Platform | Tone | |---|---| | **LinkedIn** | Thoughtful and professional. Still clear and direct, but more considered \u2014 longer sentences are fine when they add value. Thought leadership, data, and team stories work best here. | | **X (Twitter)** | Sharp and punchy. Strong opinions, short sentences. Use numbers and contrast. Threads work well for multi-part ideas. | | **Instagram** | Warm and visual. The image carries the message. Caption adds colour and context. This is where team culture and behind-the-scenes content lives. |  ---  ## Key messages  These are the ideas Loopin comes back to across all platforms.  1. **Meetings are a default, not a necessity.** Most syncs can be replaced by a short async update. 2. **Async is not slower \u2014 it's calmer.** People do better work when they control their time. 3. **Teams that communicate async move faster.** Less interruption = more deep work = faster output. 4. **Loopin makes async easy.** Quick to record, easy to share, simple to review.  ---  ## Content pillars  | Pillar | Description | Examples | |---|---|---| | **Meeting culture** | Challenge the assumption that meetings are necessary | \"5 types of meetings that should be a video\" | | **Async tips** | Practical advice for teams switching to async work | \"How to run a standup without a Zoom call\" | | **Product** | Feature highlights and product updates (not salesy \u2014 show the value) | \"New feature: Digest view \u2014 catch up on what matters in 5 min\" | | **Social proof** | Customer stories, quotes, and use cases | \"How Monzo reduced standup time by 60% using async video\" | | **Team culture** | Loopin's own remote culture \u2014 how they practice what they preach | \"Our team of 80 has zero recurring meetings\" |  ---  ## Language guide  ### Use  - Async, asynchronous - Video update, quick video, short video - Team alignment, stay aligned - Deep work, focused time - Record once, watch anytime - Cut the call, skip the meeting - On your own schedule - Remote-first, hybrid  ### Avoid  | Avoid | Why | |---|---| | \"Hop on a call\" | Contradicts the whole brand | | \"Quick sync\" | Implies a meeting, which is what Loopin replaces | | \"Touch base\" | Generic corporate speak | | \"Circle back\" | Same \u2014 tired, corporate | | \"Leverage\" | Jargon, not human | | \"Streamline\" / \"Optimize\" / \"Utilise\" | Over-used B2B filler | | \"Transform your workflow\" | Too generic, sounds like everyone else | | \"Revolutionary\" / \"Game-changing\" | Overpromises, sounds insecure | | \"Solutions\" (as a standalone noun) | Vague and hollow | | Passive voice where active is possible | \"Updates are recorded\" \u2192 \"You record updates\" |  ---  ## Visual identity  ### Colours  | Role | Hex | Use | |---|---|---| | Primary | `#5B4AE8` | Main brand colour \u2014 buttons, key UI elements, overlays | | Secondary | `#00D9C8` | Accent highlights, icons, supporting elements | | Warm accent | `#FF5A1F` | CTAs, highlights, energy moments | | Light background | `#F3F4F6` | Cards, neutral fills | | Dark | `#111827` | Text on light backgrounds | | White | `#FFFFFF` | Main background |  ### Typography  - **Headings:** Inter Bold or similar geometric sans-serif - **Body:** Inter Regular - Clean and modern. No serif fonts in digital content.  ### Image style  **What works:**  - Real people in real work environments (home offices, co-working, casual settings) - Laptop or phone screens - Small teams collaborating - Natural light, neutral tones, clean backgrounds - Close-up of someone recording a short video on their laptop - Abstract illustrations with the brand colour palette (no clip art)  **What to avoid:**  - Stock photo clich\u00e9s: people in suits pointing at whiteboards, handshakes, overly staged meetings - Overly bright or saturated photos that feel generic - Crowded group photos that look like corporate stock imagery - Dark or moody aesthetics \u2014 this is a calm, clear brand - Zoom call screenshots or anything that implies meeting culture  ### Platform-specific visual dimensions  | Platform | Format | Dimensions | |---|---|---| | LinkedIn | Single image or carousel | 1200 \u00d7 627 px (landscape) or 1080 \u00d7 1080 px (square) | | X (Twitter) | Single image | 1600 \u00d7 900 px (landscape) or 1080 \u00d7 1080 px (square) | | Instagram | Feed post | 1080 \u00d7 1080 px (square) or 1080 \u00d7 1350 px (portrait) |  ### Text on images  - Keep it minimal. One headline line maximum. - Use white or `#111827` text depending on background. - Always leave enough contrast for readability. - Never put small body text directly on a photo background.   ---  ### Composition patterns for AI image generation  Use these patterns when generating Marcus's image prompts.  **Stat graphic** - Use for data-driven posts  - One large number or percentage in the centre - Small label below the number (e.g. \"of an exec's week spent in meetings\") - Loopin wordmark or \"Loopin.co\" in small text at the bottom right - Subtle geometric shape in the background (a circle or horizontal bar in the secondary teal) - Aspect ratio: 1080 \u00d7 1080 px (square)  **Product screenshot** - Use for product updates and feature announcements - Loopin UI mockup (dark mode, as described above) shown on a laptop or monitor - Placed at an angle (3/4 view) on a light background (`#F3F4F6`) with subtle shadow - One short text line at the top: the feature name or benefit - Aspect ratio: 1200 \u00d7 627 px (landscape for LinkedIn, 1080 \u00d7 1080 for square)  **People/culture photo** - Use for team culture posts - Real-looking photo aesthetic - Natural light, neutral tones, warm but not oversaturated - Optional overlay: thin colour block in primary purple with a 1\u20132 word label (e.g. \"async first\") - Aspect ratio: 1080 \u00d7 1080 px or 1080 \u00d7 1350 px portrait  **Carousel slide** - Use for how-to and multi-step posts - Slide 1: Title slide \u2014 large bold text on primary purple (`#5B4AE8`) background, white text, secondary teal accent line at the bottom - Slides 2\u2013N: White background, numbered heading in primary purple, 2\u20133 lines of body text in dark (`#111827`), optional small icon or illustration on the right - Slide N+1 (final): CTA slide \u2014 same style as title slide - Aspect ratio: 1080 \u00d7 1080 px per slide  ---  ## Brand do's and don'ts  | Do | Don't | |---|---| | Take a clear side (async > sync) | Hedge or try to appeal to everyone | | Use real numbers when possible | Make vague claims without evidence | | Address one idea per post | Try to say everything at once | | Write for the decision-maker who agrees | Write for the sceptic who needs convincing | | Show, don't tell (product screenshots, results) | Just tell people what Loopin does | | Write how a smart person talks | Write how a marketing deck reads |"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "450077ca-b954-459f-adae-136c657f8b12",
      "name": "Sofia Research Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -2256,
        1056
      ],
      "parameters": {
        "text": "=Generate 3 content angle options for a {{ $json.content_type }} post on {{ $json.platform }}.\n{{ $json.topic_hint ? 'Brief seed idea: ' + $json.topic_hint : 'The angle is open \u2014 choose the strongest direction based on your expertise.' }}\n{{ $json.tone_note ? 'Tone guidance: ' + $json.tone_note : '' }}\n\nFor each angle provide:\n**Angle [N]: [Title \u2014 max 8 words]**\n- Insight: [The core truth this angle surfaces \u2014 1 sentence]\n- Hook concept: [How to open the post \u2014 1 sentence]\n- Why it fits: [Why this works for Loopin's ICP right now \u2014 1 sentence]\n- Platform fit: [Why this format suits {{ $json.platform }} specifically \u2014 1 sentence]\n\n",
        "options": {
          "systemMessage": "=You are Sofia, a senior content strategist at Relay.\n\nCLIENT BRAND GUIDE:\n{{ $json.brand_guide }}\n\nPLATFORM: {{ $json.platform }}\nPLATFORM TONE: {{ $json.tone_platform }}\nCONTENT TYPE: {{ $json.content_type }}\n\n{{ $json.lastFeedback ? 'PREVIOUS REVISION FEEDBACK: ' + $json.lastFeedback + '\\n\\nThe previous angles were rejected. Use this feedback to produce genuinely different angles.' : '' }}\n\nPropose angles that are specific, creative, and platform-native. Write for decision-makers at remote SaaS startups. These people already believe meetings are broken \u2014 they want validation and practical help."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "c9f7ead5-efba-407f-b64e-ceb642a68bdc",
      "name": "Sofia Save Angles",
      "type": "n8n-nodes-base.set",
      "position": [
        -1952,
        1056
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sc1",
              "name": "sofia_angles",
              "type": "string",
              "value": "={{ $json.output }}"
            },
            {
              "id": "f3abb18b-5759-4f41-bcdb-157ab4e10393",
              "name": "sofia_iteration",
              "type": "number",
              "value": "={{ $('Config \u2014 Brand + Brief').item.json['sofia_iteration '] +1}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ce429568-4dd6-4b61-98a5-adf394da7dce",
      "name": "Sofia Review Angles (Human Review)",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1760,
        1056
      ],
      "parameters": {
        "resume": "form",
        "options": {},
        "formTitle": "Sofia \u2014 Review Content Angles",
        "formFields": {
          "values": [
            {
              "fieldType": "dropdown",
              "fieldLabel": "Decision",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Approve \u2014 send to Marcus"
                  },
                  {
                    "option": "Revise \u2014 request new angles"
                  },
                  {
                    "option": "Reject \u2014 brief is off-target"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Approved angle",
              "placeholder": "If approving: paste the angle title or describe the chosen direction."
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Revision notes",
              "placeholder": "If revising: what should be different? What angle should Sofia explore instead?"
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Reject reason",
              "placeholder": "Why is this brief fundamentally unsuitable? (shown in rejection notification)"
            }
          ]
        },
        "formDescription": "=**Brief:** {{ $('Config \u2014 Brand + Brief').item.json.client }} \u00b7 {{ $('Config \u2014 Brand + Brief').item.json.platform }} \u00b7 {{ $('Config \u2014 Brand + Brief').item.json.content_type }}\n**Topic:** {{ $json.topic_hint || '(open brief)' }}\n**Tone note:** {{ $json.tone_note || '(none)' }}\n**Iteration:** {{ $json.sofia_iteration }}\n\n---\n\n{{ $json.sofia_angles }}\n\n---\nSelect a decision below. If approving, copy the angle title into the field. If revising, explain specifically what needs to change."
      },
      "typeVersion": 1.1
    },
    {
      "id": "cee62f7d-cc85-4065-b5d4-724939edc013",
      "name": "Sofia Route Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1552,
        1056
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "b879ec5b-49f3-4594-baed-6cea8d77d36d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Approve \u2014 send to Marcus"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e7485247-a0da-42c8-9a09-4d9f321c4769",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.Decision }}",
                    "rightValue": "Revise \u2014 request new angles"
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "allMatchingOutputs": true
        }
      },
      "typeVersion": 3
    },
    {
      "id": "1d6fd14b-1a3c-4a3f-bfe1-5a40d7c28cf5",
      "name": "Marcus \u2014 Creative Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1072,
        1040
      ],
      "parameters": {
        "text": "=Write a {{ $json.platform }} post for {{ $json.brand_name }} based on this approved angle:\n\nANGLE: {{ $json.approved_angle }}\n\n{{ $json.tone_note ? 'Tone guidance: ' + $json.tone_note : '' }}\n{{ $json.marcus_iteration > 0 ? 'Iteration ' + $json.marcus_iteration + ' \u2014 revise based on feedback above.' : '' }}\n\nProduce your output in this exact format:\n\n---POST COPY---\n[Complete, publish-ready post. Correct length for {{ $json.platform }} per the platform specs. Hook on line 1 \u2014 must work before the \"see more\" cutoff. CTA on the last line.]\n\n---HASHTAGS---\n[Correct number for {{ $json.platform }} per the platform specs. Placed at end.]\n\n---VISUAL DIRECTION---\n[3\u20134 sentences. Describe: subject, composition, lighting, and which brand colours to use (#5B4AE8 primary purple, #00D9C8 teal accent, #FF5A1F warm orange). Be specific enough for a designer to execute immediately. Reference the visual style: {{ $json.visual_style }}]\n\n---COMPOSITION PATTERN---\n[Choose one: Stat graphic / Product screenshot / People & culture photo / Carousel slide. Explain in one sentence why this pattern fits this post.]",
        "options": {
          "systemMessage": "=You are Marcus, a creative specialist at Relay, a B2B social media agency.\n\n=== LOOPIN BRAND GUIDE ===\n{{ $json.brand_guide }}\n\n=== PLATFORM SPECIFICATIONS ===\n{{ $json.platform_specs }}\n\n=== THIS BRIEF ===\nClient: {{ $json.brand_name }}\nPlatform: {{ $json.platform }}\nPlatform tone: {{ $json.tone_platform }}\nContent type: {{ $json.content_type }}\nLanguage to avoid: {{ $json['language_avoid '] }}\nVisual style: {{ $json.visual_style }}\n\n{{ $json.lastFeedback ? '=== REVISION FEEDBACK ===\\n' + $json.lastFeedback + '\\n\\nThe previous draft was returned. Address every point in this feedback specifically.' : '' }}\n\nYour job: write publish-ready copy and clear visual direction.\nObey all platform specs above \u2014 character limits, hashtag counts, hook placement.\nMatch the brand voice exactly \u2014 check the language avoid list before submitting."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "2407ea3a-d9bb-4b21-a24f-096dbf8858a0",
      "name": "Taylor \u2014 Review Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        160,
        1024
      ],
      "parameters": {
        "text": "=Review the following content for {{ $json.brand_name }} on {{ $json.platform }}.\n\n=== ORIGINAL BRIEF ===\nClient: {{ $json.client }}\nPlatform: {{ $json.platform }}\nContent type: {{ $json.content_type }}\nTopic hint: {{ $json.topic_hint || '(none provided)' }}\nTone note: {{ $json.tone_note || '(none provided)' }}\n\n=== APPROVED ANGLE (from Sofia) ===\n{{ $json.approved_angle }}\n\n=== POST COPY ===\n{{ $json.post_copy }}\n\n=== HASHTAGS ===\n{{ $json.hashtags }}\n\n=== VISUAL DIRECTION ===\n{{ $json.visual_direction }}\n\n=== COMPOSITION PATTERN ===\n{{ $json.composition_pattern }}\n\n{{ $json.lastFeedback ? '=== PREVIOUS FEEDBACK HISTORY ===\\n' + $json.lastFeedback + '\\n\\nNote: This content has already been revised once. Check whether the previous feedback was actually addressed.' : '' }}\n\nReturn your audit as valid JSON only:\n{\n  \"ai_summary\": \"2\u20133 sentences. What the post says and what it is trying to achieve.\",\n  \"character_count\": \"Exact count of post_copy. State whether within {{ $json.platform }} optimal range per platform specs.\",\n  \"quality_flags\": [\n    \"Each flag on its own line. Be specific \u2014 name the exact issue, quote the offending phrase if relevant.\"\n  ],\n  \"brand_alignment_score\": \"1\u20135 with one reason\",\n  \"recommendation\": \"APPROVE or REVISE_TO_MARCUS or REJECT_TO_SOFIA\",\n  \"reasoning\": \"One sentence explaining the recommendation.\",\n  \"feedback_for_team\": \"Written directly to Marcus or Sofia. Tell them exactly what to fix or rethink. Specific enough to act on immediately.\",\n  \"final_score\": 0\n}",
        "options": {
          "systemMessage": "=You are Taylor, the final quality reviewer at Relay, a B2B social media agency.\n\n=== LOOPIN BRAND GUIDE ===\n{{ $json.brand_guide }}\n\n=== PLATFORM SPECIFICATIONS ===\n{{ $json.platform_specs }}\n\nYour role is to audit, not rewrite. Make a clear routing decision based on evidence.\n\nROUTING LOGIC:\n- APPROVE \u2014 Content is publish-ready. Score 8\u201310. Minor wording tweaks at most.\n- REVISE_TO_MARCUS \u2014 Strategy is sound, execution needs work. Score 5\u20137. Examples: weak hook, off-brand tone, wrong length, unclear CTA, visual mismatch, language from the avoid list.\n- REJECT_TO_SOFIA \u2014 The angle itself is wrong. Score 1\u20134. Examples: wrong audience, irrelevant topic, generic positioning that could be any SaaS brand, fundamentally off-brief.\n\nREVIEW CHECKLIST \u2014 evaluate every item:\nHook: Does line 1 stop scrolling? Does it work before the platform cutoff?\nValue proposition: Is the benefit clear within the first 3 lines?\nBrand voice: Short sentences, active voice, direct, human \u2014 no jargon?\nLanguage avoid check: None of these words appear \u2014 {{ $json['language_avoid '] }}\nAudience fit: Written for remote/hybrid SaaS team leads, not a generic business audience?\nPlatform compliance: Length, hashtag count, formatting \u2014 all within spec?\nCTA: One clear ask at the end? Natural, not forced?\nHuman feel: Does this read like a real person wrote it, not an AI?\nVisual alignment: Does the visual direction match and reinforce the post message?\nBrief answer: Does the post actually answer what was originally requested?\n\nOUTPUT RULES:\n- Return ONLY valid JSON. No preamble, no markdown fences, no explanation outside the JSON.\n- quality_flags: specific, short bullet phrases \u2014 name the exact issue and line if possible.\n- recommendation: exactly one of APPROVE, REVISE_TO_MARCUS, REJECT_TO_SOFIA \u2014 no other values.\n- final_score: integer 1\u201310.\n- feedback_for_team: written directly to Marcus (if REVISE) or Sofia (if REJECT) \u2014 actionable, specific, not vague.\n\n Return ONLY the raw JSON object.\nNo markdown code fences. No json. No  at start or end.\nStart your response with { and end with }\n\n\nOUTPUT RULES:\nReturn valid JSON with these exact field names (case-sensitive):\n{\n  \"ai_summary\": \"string\",\n  \"character_count\": \"string\",\n  \"quality_flags\": [\"string\", \"string\"],\n  \"brand_alignment_score\": \"number (1-5)\",\n  \"recommendation\": \"string (APPROVE or REVISE_TO_MARCUS or REJECT_TO_SOFIA)\",\n  \"reasoning\": \"string\",\n  \"feedback_for_team\": \"string\",\n  \"final_score\": \"number (1-10)\"\n}"
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "1e54dac5-f607-450b-85ab-ba280b7ca41f",
      "name": "Output Parser",
      "type": "n8n-nodes-base.code",
      "position": [
        512,
        1024
      ],
      "parameters": {
        "jsCode": "const output = $json.output;\n\n// Strip markdown code fences\nconst cleanJson = output\n  .replace(/```json\\n?/g, '')\n  .replace(/```\\n?/g, '')\n  .trim();\n\n// Parse JSON\nconst parsed = JSON.parse(cleanJson);\n\nreturn [{\n  json: {\n    taylor_summary: parsed.ai_summary || '',\n    taylor_char_count: parsed.character_count || '',\n    taylor_flags: Array.isArray(parsed.quality_flags) \n      ? parsed.quality_flags.join('\\n\u2022 ') \n      : parsed.quality_flags || '',\n    taylor_brand_alignment: parsed.brand_alignment_score || 0,\n    taylor_recommendation: parsed.recommendation || '',\n    taylor_reasoning: parsed.reasoning || '',\n    taylor_feedback: parsed.feedback_for_team || '',\n    taylor_score: parsed.final_score || 0,\n    // Pass through original data for Wait form\n    post_copy: $json.post_copy || '',\n    visual_direction: $json.visual_direction || ''\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ec8b37a3-eba5-4a34-8b1e-1e9cdcc22d9e",
      "name": "Merge Taylor Output",
      "type": "n8n-nodes-base.set",
      "position": [
        736,
        1024
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "91c4dc28-c492-4999-8dcb-cfc5a197a0aa",
              "name": "post_copy",
              "type": "string",
              "value": "={{ $('Taylor \u2014 Merge Context').item.json.post_copy }}"
            },
            {
              "id": "e70e2616-b159-4526-86d4-04d82b42ab9a",
              "name": "visual_direction",
              "type": "string",
              "value": "={{ $('Taylor \u2014 Merge Context').item.json.visual_direction }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "b2d8096c-a385-4ce0-af26-0822fbb9cfea",
      "name": "Log Published Post",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1440,
        1040
      ],
      "parameters": {
        "columns": {
          "value": {
            "row_number": 2,
            "Published Post": "={{ $('Merge Taylor Output').item.json.post_copy }}"
          },
          "schema": [
            {
              "id": "client",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "client",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "brand_name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "brand_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "brand_voice",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "brand_voice",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tone_linkedin",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "tone_linkedin",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tone_x",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "tone_x",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tone_instagram",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "tone_instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Published Post",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Published Post",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1981106103,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA/edit#gid=1981106103",
          "cachedResultName": "Output"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/17-OLxkWCGtFgUqL1wrYZ8qZLeSf8cNJxd61txisEjvA/edit?usp=drivesdk",
          "cachedResultName": "test-briefs-form-data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c7af56cf-58d8-4be9-ac8c-3768e3a98771",
      "name": "Sofia Feedback",
      "type": "n8n-nodes-base.set",
      "position": [
        -1376,
        1744
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sf1",
              "name": "lastFeedback",
              "type": "string",
              "value": "={{ $json['Revision notes'] || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6c4a2526-52cb-4177-b8ae-68b69111b242",
      "name": "Marcus Feedback",
      "type": "n8n-nodes-base.set",
      "position": [
        -96,
        1504
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sf1",
              "name": "lastFeedback",
              "type": "string",
              "value": "={{ $json['Revision notes'] || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "c6981473-2b66-4362-8314-b232a13b169b",
  "connections": {
    "Output Parser": {
      "main": [
        [
          {
            "node": "Merge Taylor Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sofia Feedback": {
      "main": [
        [
          {
            "node": "Sofia Research Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcus Feedback": {
      "main": [
        [
          {
            "node": "Sofia Research Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Post Draft": {
      "main": [
        [
          {
            "node": "Marcus \u2014 Review Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Brief Intake Form": {
      "main": [
        [
          {
            "node": "Client DB Lookup (Sheets)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sofia Save Angles": {
      "main": [
        [
          {
            "node": "Sofia Review Angles (Human Review)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Taylor Output": {
      "main": [
        [
          {
            "node": "Taylor \u2014 Review Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sofia Research Agent": {
      "main": [
        [
          {
            "node": "Sofia Save Angles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sofia Route Decision": {
      "main": [
        [
          {
            "node": "Marcus \u2014 Merge Context",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sofia Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcus Route Decision": {
      "main": [
        [
          {
            "node": "Taylor \u2014 Merge Context",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Marcus Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Taylor Route Decision": {
      "main": [
        [
          {
            "node": "Log Published Post",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Marcus \u2014 Creative Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sofia Research Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcus \u2014 Review Draft": {
      "main": [
        [
          {
            "node": "Marcus Route Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Taylor \u2014 Review Agent": {
      "main": [
        [
          {
            "node": "Output Parser",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Taylor \u2014 Review Draft": {
      "main": [
        [
          {
            "node": "Taylor Route Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config \u2014 Brand + Brief": {
      "main": [
        [
          {
            "node": "Sofia Research Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Sofia Research Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Marcus \u2014 Merge Context": {
      "main": [
        [
          {
            "node": "Marcus \u2014 Creative Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Taylor \u2014 Merge Context": {
      "main": [
        [
          {
            "node": "Taylor \u2014 Review Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Client DB Lookup (Sheets)": {
      "main": [
        [
          {
            "node": "Config \u2014 Brand + Brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Marcus \u2014 Creative Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Taylor \u2014 Review Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Marcus \u2014 Creative Agent": {
      "main": [
        [
          {
            "node": "Save Post Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sofia Review Angles (Human Review)": {
      "main": [
        [
          {
            "node": "Sofia Route Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}