<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: The Catalyst Project</title>
    <description>The latest articles on DEV Community by The Catalyst Project (@catalystproject).</description>
    <link>https://dev.to/catalystproject</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3860366%2Fb091109c-3a44-442a-8d14-9db0fc7deb1b.png</url>
      <title>DEV Community: The Catalyst Project</title>
      <link>https://dev.to/catalystproject</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/catalystproject"/>
    <language>en</language>
    <item>
      <title>I Built an AI Content Pipeline That Publishes 4 SEO-Optimized Articles Per Day — Here's the Architecture</title>
      <dc:creator>The Catalyst Project</dc:creator>
      <pubDate>Sat, 04 Apr 2026 03:51:53 +0000</pubDate>
      <link>https://dev.to/catalystproject/i-built-an-ai-content-pipeline-that-publishes-4-seo-optimized-articles-per-day-heres-the-345a</link>
      <guid>https://dev.to/catalystproject/i-built-an-ai-content-pipeline-that-publishes-4-seo-optimized-articles-per-day-heres-the-345a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplj8q22xkypknkvnwcu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplj8q22xkypknkvnwcu4.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  I Built an AI Content Pipeline That Publishes 4 SEO-Optimized Articles Per Day — Here's the Architecture
&lt;/h1&gt;

&lt;p&gt;I'm a chemical engineer who taught himself to code. Six months ago I started building &lt;a href="https://catalystproject.ai" rel="noopener noreferrer"&gt;Catalyst OS&lt;/a&gt; — a life optimization platform with 106 free calculators, 225 interactive learning modules, and a premium AI journaling tool. The problem was content. I needed hundreds of articles to drive organic traffic, and writing them manually at 2-3 hours each wasn't going to work.&lt;/p&gt;

&lt;p&gt;So I built an automated content pipeline that generates, publishes, optimizes for SEO, pings search engines, generates social posts, and notifies me via Telegram — four times a day, zero manual intervention.&lt;/p&gt;

&lt;p&gt;Here's exactly how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n&lt;/strong&gt; (self-hosted workflow automation) — orchestrates everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Sonnet 4&lt;/strong&gt; (Anthropic API) — generates the actual content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (PostgreSQL) — stores articles, topics, and metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15&lt;/strong&gt; — renders articles with SSR and structured data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IndexNow API&lt;/strong&gt; — pings Bing/Yandex for instant indexing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt; — transactional email&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram Bot API&lt;/strong&gt; — real-time notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐     ┌──────────────┐     ┌─────────────────┐
│  Schedule    │────→│  Topic Bank  │────→│  Config Merge   │
│  (4x daily)  │     │  (Supabase)  │     │  (prompts +     │
└─────────────┘     └──────────────┘     │   guardrails)   │
                                          └────────┬────────┘
                                                   │
                                          ┌────────▼────────┐
                                          │  Claude Sonnet 4 │
                                          │  (generation)    │
                                          └────────┬────────┘
                                                   │
                    ┌──────────────────────────────┤
                    │                              │
           ┌───────▼───────┐              ┌───────▼───────┐
           │  Parse + SEO   │              │  OG Image     │
           │  Field Extract  │              │  Generation   │
           └───────┬───────┘              └───────────────┘
                    │
           ┌───────▼───────┐
           │  Supabase      │
           │  INSERT         │
           └───────┬───────┘
                    │
        ┌──────────┼──────────┐
        │          │          │
  ┌─────▼──┐ ┌────▼───┐ ┌───▼────┐
  │IndexNow│ │Social  │ │Telegram│
  │  Ping  │ │ Posts  │ │ Notify │
  └────────┘ └────────┘ └────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: The Topic Bank
&lt;/h2&gt;

&lt;p&gt;I don't let the AI decide what to write about. I maintain a &lt;code&gt;topic_bank&lt;/code&gt; table in Supabase with pre-planned topics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;topic_bank&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- mind, body, heart, wealth, spirit&lt;/span&gt;
  &lt;span class="n"&gt;concept&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;target_audience&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content_type_slug&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'available'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;used_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Postgres function &lt;code&gt;get_next_topic_for_generation()&lt;/code&gt; picks the next available topic, marks it as processing, and returns it. This prevents duplicate generation if two runs overlap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: The Prompt Engineering
&lt;/h2&gt;

&lt;p&gt;This is where most AI content pipelines fall apart. They use a generic "write an article about X" prompt and get generic garbage back. My prompt is ~16,000 characters and includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brand voice rules&lt;/strong&gt; — no AI-isms ("delve," "unleash," "game-changer"), no filler paragraphs, every claim needs a specific number or citation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;104 calculator links&lt;/strong&gt; organized by dimension — Claude weaves 3-8 relevant internal links naturally into each article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BODY_CALCS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- [TDEE Calculator](https://catalystproject.ai/calculators/body/tdee) — daily energy expenditure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- [Macro Calculator](https://catalystproject.ai/calculators/body/macros) — protein/carb/fat targets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 27 more&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Featured snippet optimization&lt;/strong&gt; — question-format headers, numbered lists, bold definitions. These are the patterns Google pulls for position zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured output format&lt;/strong&gt; — the prompt requires 13 labeled sections (meta description, subtitle, keywords, hook, problem statement, main content, key takeaways, action steps, success metrics, time to results, evidence level, sources, primary action). The parser extracts each one into its own database column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Content Quality Enforcement
&lt;/h2&gt;

&lt;p&gt;The generation node uses Claude Sonnet 4 with &lt;code&gt;temperature: 0.7&lt;/code&gt;. After generation, a Code node parses the response and validates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Extract all 13 sections via regex&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metaMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/## Meta Description&lt;/span&gt;&lt;span class="se"&gt;\n([\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)(?=\n&lt;/span&gt;&lt;span class="sr"&gt;## &lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keywordsMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/## Keywords&lt;/span&gt;&lt;span class="se"&gt;\n([\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)(?=\n&lt;/span&gt;&lt;span class="sr"&gt;## &lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... etc&lt;/span&gt;

&lt;span class="c1"&gt;// Quality checks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wordCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasInternalLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/catalystproject&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;ai/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasSpecificData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+%|&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+ &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;study|studies|participants|patients&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wordCount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hasInternalLinks&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasSpecificData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Quality check failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every article gets a &lt;code&gt;featured_image_url&lt;/code&gt; set at birth via a dynamic OG image endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/og?title={title}&amp;amp;subtitle={dimension}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: SEO That Actually Works
&lt;/h2&gt;

&lt;p&gt;Each article is stored with structured metadata that the Next.js page consumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Automatic on every article page&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleJsonLd&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BreadcrumbJsonLd&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// OpenGraph&lt;/span&gt;
&lt;span class="nx"&gt;publishedTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modifiedTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authors&lt;/span&gt;

&lt;span class="c1"&gt;// Robots&lt;/span&gt;
&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;large&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sitemap regenerates with articles at priority 0.85. An IndexNow endpoint pings Bing, Yandex, and Seznam within seconds of publication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// POST /api/indexnow&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`https://catalystproject.ai&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.indexnow.org/indexnow`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;catalystproject.ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;urlList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Social Distribution
&lt;/h2&gt;

&lt;p&gt;After the article is inserted, a separate workflow generates platform-specific social posts (Twitter, LinkedIn) and stores them in a &lt;code&gt;content_pieces&lt;/code&gt; table. A scheduled LinkedIn Publisher workflow picks up unposted pieces and publishes them via the LinkedIn REST API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Telegram Notification
&lt;/h2&gt;

&lt;p&gt;Every generated article triggers a Telegram message with title, word count, internal link count, and a direct link to the published article. I review every piece even though it's automated — quality control matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results After 3 Months
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;206 published articles&lt;/strong&gt; across 5 dimensions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;106 calculator pages&lt;/strong&gt; with dimension-aware CTAs on every article&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured data&lt;/strong&gt; on every page (Article, BreadcrumbList, FAQPage, ProfessionalService)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4 articles/day&lt;/strong&gt; with zero manual writing&lt;/li&gt;
&lt;li&gt;Average article: &lt;strong&gt;1,200-1,800 words&lt;/strong&gt; with 4-6 internal links each&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with Google Search Console on day one.&lt;/strong&gt; I built 200+ articles before submitting my sitemap. Those articles sat unindexed for weeks. Submit your sitemap before you have content — Google will discover new pages as they appear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't trust &lt;code&gt;temperature: 1.0&lt;/code&gt; for production content.&lt;/strong&gt; Higher temperatures produce more creative writing but also more hallucinated citations and inconsistent formatting. 0.7 is the sweet spot for reliable, parseable output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal linking is an architectural decision, not a content decision.&lt;/strong&gt; Embedding all 104 calculator URLs into the system prompt means every article links to relevant tools without the AI needing to "remember" them. The linking happens at the prompt level, not the content level.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Stack
&lt;/h2&gt;

&lt;p&gt;The entire platform runs on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15&lt;/strong&gt; + React 19 + TypeScript on Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (PostgreSQL + pgvector for RAG)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n8n Cloud&lt;/strong&gt; (7 workflows: content gen, social posting, lead scraping, enrichment, email drafting, pipeline snapshots, LinkedIn publishing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude API&lt;/strong&gt; for content generation and AI chat&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; for subscriptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt; for email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total monthly infrastructure cost: ~$150. No employees. One codebase.&lt;/p&gt;




&lt;p&gt;If you're building something similar or want to see the calculators and content in action, check out &lt;a href="https://catalystproject.ai" rel="noopener noreferrer"&gt;catalystproject.ai&lt;/a&gt;. The &lt;a href="https://catalystproject.ai/consulting" rel="noopener noreferrer"&gt;consulting page&lt;/a&gt; has details on how I build these systems for other businesses.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the architecture, prompt engineering, or n8n workflow design in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
