An n8n workflow that takes one raw idea in and produces a coordinated LinkedIn post, X thread, newsletter, carousel plan and short-form script out, all traceable to the same idea and campaign record, with nothing publishing until a human approves it.
Built for content leads and solo operators who have one good idea and no reliable way to turn it into a full multi-platform campaign without losing a week to manual drafting.
<aside> ⚡
Want your version wired up and running, with your own tools and approval flow already loaded? Talk to UGC Ninja
</aside>
<aside> 📁
| # | Node | Type | Purpose |
|---|---|---|---|
| 1 | Idea Intake | Webhook / Form Trigger | Captures the raw idea, source, and campaign name |
| 2 | Create Campaign Record | Airtable/Postgres Insert | Opens the campaign row, generates campaign_id |
| 3 | Audience Research | HTTP Request + AI node | Pulls recent engagement signals tied to the idea's topic |
| 4 | Angle Selection | AI node | Proposes 3 angles, scores them, picks one with reasoning |
| 5 | Core Argument Development | AI node | Builds the argument skeleton every output will share |
| 6 | Split | NoOp / Split In Batches | Fans the argument out to five drafting nodes at once |
| 7a | LinkedIn Drafting | AI node | Full post draft |
| 7b | X Thread Creation | AI node | 6 to 10 tweet thread |
| 7c | Newsletter Drafting | AI node | Section-based newsletter draft |
| 7d | Carousel Planning | AI node | Slide-by-slide plan, text only |
| 7e | Short-Form Scripting | AI node | 30 to 60 second script with beats |
| 8 | Creative Briefing | AI node | One brief per visual asset needed |
| 9 | Merge | Merge node | Recombines all five outputs under one campaign_id |
| 10 | Quality Control | AI node | Checks every output against the QC rubric |
| 11 | Human Approval Gate | Wait node + Slack/Email | Sends all outputs to a reviewer, halts until a response arrives |
| 12 | Scheduling | HTTP Request | Only fires on approval, writes schedule times back to the record |
| 13 | Performance Tracking | Cron + HTTP Request | Pulls engagement stats on a schedule, writes back to the campaign row |
<aside> 📁
Branch logic. Node 6 splits into five parallel branches (7a-7e) that all read the same core_argument field from node 5, so a change to the argument never has to be copied five times. Node 9 merges on campaign_id, not on timing, so a slow branch never gets dropped.
</aside>
<aside> ⚠️
Retry rules. Every AI node gets 3 retry attempts, exponential backoff starting at 10 seconds. A node that fails all 3 writes status: failed_node_X to the campaign record and the workflow stops rather than continuing with a gap.
Error handling. A separate Error Trigger workflow watches the main one, posts any unhandled failure to a Slack error channel with the campaign_id and failed node name, and sets the campaign record to needs_attention. Nothing retries automatically past 3 attempts, because a silent retry loop on a broken prompt burns credits for no output.
</aside>
One table, campaigns, is the spine. Every output row links back to it by campaign_id, which is what keeps five parallel drafts traceable to the one original idea.
| Field | Type | Notes |
|---|---|---|
| campaign_id | Auto-number / UUID | Primary key |
| raw_idea | Long text | The original input, never edited |
| source | Single select | Call, article, brainstorm, client |
| angle_chosen | Text | From node 4 |
| core_argument | Long text | From node 5 |
| status | Single select | drafting, qc, awaiting_approval, approved, scheduled, published, needs_attention |
| approved_by | Text | Filled at node 11 |
| approved_at | Date |
| Field | Type | Notes |
|---|---|---|
| output_id | UUID | Primary key |
| campaign_id | Link to campaigns | Foreign key |
| platform | Single select | LinkedIn, X, Newsletter, Carousel, Short-form |
| draft_text | Long text | |
| qc_result | Single select | Pass, Flagged |
| scheduled_time | Date/time | Written by node 12, blank until approved |
| published | Checkbox |
<aside> 📁
Required to trigger the workflow: raw_idea and source. Everything else populates as the workflow runs. No output node fires without a valid campaign_id.
</aside>