CONTENTENGINE

Builder Spec (PRD) — v1.0 — February 2026

TARGET: AI Coding Agent (Claude Code / Ralph Loops)

NOT FOR HUMAN READING — See CONTENTENGINE-GUIDE.html for human steps

Source: claude.ai/chat/f7a01dae-3d5b-40d7-b3de-d37b88505625

📋 PRD 🧑‍💻 Human Guide 🏗️ ADR

1. Project Overview

This PRD instructs Claude Code to build an automated X/Twitter content engine consisting of n8n workflows, Supabase tables, and LLM integrations that scrape target accounts via Apify, generate engagement replies, and score draft posts against X's published algorithm weights. The system operates in two layers: Layer 1 (n8n automation) handles data ingestion and reply generation; Layer 2 (LLM API) handles content scoring and voice-matched generation.

1.1 What This System Does

Layer 1 — Data & Automation (n8n)

Layer 2 — Intelligence (LLM API)

Shared Infrastructure

1.2 Architecture Summary

All automation runs on existing n8n instance. Supabase provides database. No VPS, no Docker, no custom backend required. LLM calls made directly from n8n HTTP Request nodes.

ComponentPurposePort
n8n (existing)All workflow automation — scraping, reply gen, posting, digest443 (n8n.masterymade.com)
Supabase (free tier)Database for targets, scraped posts, replies, voice profileN/A (hosted)
ApifyX/Twitter data scraping — timelines, profiles, engagementN/A (API)
Anthropic APIReply generation, post scoring, content optimizationN/A (API)
X API FreePosting replies and original content (write-only)N/A (API)
Telegram Bot APIDaily digest notifications and approval workflowN/A (API)

1.3 Key Constraints

Credential Safety: NEVER put API keys, tokens, or credentials in any n8n workflow JSON, script, or prompt. All secrets stored as n8n Credentials or Supabase environment variables. Use ${VARIABLE} references.
No Custom Backend: Everything runs as n8n workflows. No FastAPI, no Express, no custom servers. If it can't be done in n8n, defer to a later phase.
No Frontend: Operator interacts via Telegram bot and Supabase dashboard. No Next.js, no React, no custom UI in Phase 1-2.
Apify TOS Awareness: Keep scraping volume to personal-use levels. Max 50 target accounts. Max 1,000 tweets/day scraped. No bulk data resale.
Human Handoff: The human creates Apify, Supabase, and Telegram accounts. Claude Code creates n8n workflow JSONs and Supabase SQL. Human imports/runs them.
Pause Protocol: PAUSE execution and output HUMAN_ACTION_REQUIRED at every point listed in Section 8.

2. Infrastructure Specification

ParameterValue
Automation Platformn8n (self-hosted at n8n.masterymade.com, existing)
DatabaseSupabase Free Tier (Postgres 15 + pgvector)
Scraping ServiceApify — Tweet Scraper V2 (apidojo/tweet-scraper)
LLM ProviderAnthropic API — claude-sonnet-4-20250514
X API TierFree (write-only: POST tweets, POST replies)
NotificationTelegram Bot API
Embedding Modeltext-embedding-3-small (OpenAI) — Phase 2 only

2.1 Resource Budget

ServiceMonthly AllocationNotes
Supabase Free Tier500MB database, 1GB storage, 2GB transferSufficient for ~50K posts + metadata
Apify~3,000 tweets/month scraped$0.40/1K = ~$1.20/mo at personal volume
Anthropic API~150 reply generations/day × 30 = 4,500/mo~$8-15/mo at Sonnet pricing
X API Free1,500 posts/month (write)Sufficient for 50 replies/day
TelegramUnlimitedFree

3. Routing / Orchestration Architecture

No model cascade. Single LLM provider (Anthropic Sonnet) for all generation tasks. Routing is workflow-based via n8n, not model-based.

3.1 Workflow Routing

Triggern8n WorkflowLLM CallOutput
Daily cron (6 AM CT)WF-01: Scrape & IngestNoneNew posts in Supabase
WF-01 completionWF-02: Reply GenerationSonnet — reply promptReply candidates in Supabase
WF-02 completionWF-03: Daily DigestNoneTelegram message to operator
Telegram callbackWF-04: Post Approved RepliesNoneReplies posted via X API
Webhook (manual)WF-05: Post ScorerSonnet — scoring promptJSON score + alternatives
Weekly cron (Sunday 6 PM)WF-06: Performance TrackerSonnet — analysis promptWeekly digest via Telegram

4. Container / Isolation Architecture

Not applicable. No containers. All execution runs within existing n8n instance. Supabase is hosted SaaS. No Docker required.

5. Security / Data Protection

Standard security practices apply. No PII handling beyond X public data. All API credentials stored in n8n's built-in credential store (encrypted at rest). Supabase Row Level Security not required for single-user deployment — add RLS in Phase 4 for multi-tenant.

6. Core Execution Engine

6.1 Workflow Definitions

FilePurpose
WF-01-scrape-ingest.jsonn8n workflow: Apify trigger → parse tweets → upsert Supabase
WF-02-reply-generation.jsonn8n workflow: Read new posts → LLM generate replies → store candidates
WF-03-daily-digest.jsonn8n workflow: Query pending replies → format → send Telegram
WF-04-post-replies.jsonn8n workflow: Telegram callback → post approved replies via X API
WF-05-post-scorer.jsonn8n workflow: Webhook input → LLM score → return JSON
WF-06-weekly-tracker.jsonn8n workflow: Aggregate metrics → LLM analysis → Telegram digest
supabase-schema.sqlComplete database schema for Supabase
prompts/reply-gen.mdSystem prompt for reply generation
prompts/post-scorer.mdSystem prompt for algorithm scoring
prompts/voice-profile.mdVoice profile template (operator fills with top posts)

6.2 WF-01 Behavior: Scrape & Ingest

  1. Cron trigger fires at 6:00 AM CT daily
  2. Query Supabase target_accounts table for all active targets
  3. For each target: call Apify Tweet Scraper API with username, maxItems=20, date filter=last 24h
  4. Parse Apify response: extract tweet ID, text, author, likes, replies, retweets, quotes, bookmarks, posted_at
  5. Upsert into Supabase scraped_posts table (dedup on x_post_id)
  6. Tag new posts with status='new'
  7. Output count of new posts to trigger WF-02

6.3 WF-02 Behavior: Reply Generation

  1. Triggered by WF-01 completion (or manual trigger)
  2. Query Supabase for posts where status='new' AND replies_generated=false
  3. Load operator voice profile from voice_profiles table
  4. For each post (batch of up to 50):
  5. Update scraped_posts replies_generated=true
  6. Trigger WF-03

6.4 WF-05 Behavior: Post Scorer

  1. Triggered by webhook (POST /webhook/score-post, body: {text: "draft post"})
  2. Build prompt: system prompt (post-scorer.md) + X algorithm weights + draft text
  3. Call Anthropic API (claude-sonnet-4-20250514, max_tokens=1000)
  4. Parse structured response:
  5. Return JSON response via webhook
  6. Optionally log to Supabase scored_drafts table

6.5 Prompt Specifications

Reply Generation Prompt (prompts/reply-gen.md)

SYSTEM: You generate engaging X/Twitter replies for a strategic engagement campaign.

RULES:
- Every reply must add VALUE (insight, data, experience, contrarian take)
- Never generic ("Great post!", "So true!", "This 🔥")
- Never sycophantic — speak as a peer, not a fan
- Match the operator's voice profile below
- Keep replies under 280 characters
- Generate exactly 3 options: (1) Agreement + extension, (2) Contrarian/question, (3) Personal experience/data

VOICE PROFILE:
{{voice_profile}}

TARGET POST:
Author: @{{target_username}}
Text: {{post_text}}
Engagement: {{likes}} likes, {{replies}} replies, {{retweets}} RTs

Generate 3 reply options as JSON array:
[{"type": "agree_extend", "text": "..."}, {"type": "contrarian", "text": "..."}, {"type": "experience", "text": "..."}]

Post Scorer Prompt (prompts/post-scorer.md)

SYSTEM: You are an X/Twitter algorithm expert. Score posts against X's published ranking algorithm weights.

X ALGORITHM WEIGHTS (from open-source codebase):
1. Reply Spark (weight 75): Probability of generating replies + author engagement
2. Conversation Depth (13.5): Will people click into conversation thread?
3. Profile Pull (12.0): Will readers visit the author's profile?
4. Conversation Dwell (10.0): Will people spend 2+ minutes on this?
5. Retweet Trigger (1.0): Is this shareable/quotable?
6. Favorite Factor (0.5): Is this likeable?
7. Hook Strength (custom): Does first line stop the scroll?
8. Format Score (custom): Proper line breaks, length, media potential?
9. Readability (custom): Clear, conversational, no "shout" patterns?

SCORING: Rate each metric 1-10. Weight the final score per algorithm weights above.

POST TO ANALYZE:
{{draft_text}}

Return JSON:
{
  "scores": {"reply_spark": N, "conversation_depth": N, ...},
  "weighted_total": N,
  "improvements": ["specific change 1", "specific change 2", "specific change 3"],
  "alternatives": ["optimized version 1", "optimized version 2"]
}

7. Database Schema

-- CONTENTENGINE Schema v1.0
-- Run in Supabase SQL Editor

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Target accounts for Reply Engine
CREATE TABLE target_accounts (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  x_username TEXT NOT NULL UNIQUE,
  display_name TEXT,
  follower_count INT,
  list_name TEXT DEFAULT 'default',  -- group targets by list
  priority INT DEFAULT 5,            -- 1=highest, 10=lowest
  active BOOLEAN DEFAULT true,
  notes TEXT,                         -- why this target matters
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Scraped posts from target accounts
CREATE TABLE scraped_posts (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  x_post_id TEXT NOT NULL UNIQUE,
  author_username TEXT NOT NULL,
  content TEXT NOT NULL,
  likes INT DEFAULT 0,
  replies INT DEFAULT 0,
  retweets INT DEFAULT 0,
  quotes INT DEFAULT 0,
  bookmarks INT DEFAULT 0,
  impressions INT,
  posted_at TIMESTAMPTZ,
  scraped_at TIMESTAMPTZ DEFAULT NOW(),
  replies_generated BOOLEAN DEFAULT false,
  status TEXT DEFAULT 'new',          -- new, processed, skipped
  created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_scraped_status ON scraped_posts(status);
CREATE INDEX idx_scraped_author ON scraped_posts(author_username);

-- Reply candidates (LLM-generated)
CREATE TABLE reply_candidates (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  scraped_post_id UUID REFERENCES scraped_posts(id),
  target_username TEXT NOT NULL,
  target_post_text TEXT,              -- denormalized for digest readability
  reply_type TEXT NOT NULL,           -- agree_extend, contrarian, experience
  reply_text TEXT NOT NULL,
  status TEXT DEFAULT 'pending',      -- pending, approved, rejected, posted, failed
  posted_at TIMESTAMPTZ,
  reply_x_post_id TEXT,               -- X post ID after posting
  created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_replies_status ON reply_candidates(status);

-- Reply performance tracking
CREATE TABLE reply_performance (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  reply_candidate_id UUID REFERENCES reply_candidates(id),
  impressions INT DEFAULT 0,
  likes INT DEFAULT 0,
  replies INT DEFAULT 0,
  profile_visits INT DEFAULT 0,
  follower_delta INT DEFAULT 0,       -- followers gained from this reply
  measured_at TIMESTAMPTZ DEFAULT NOW(),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Scored drafts (post scorer history)
CREATE TABLE scored_drafts (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  draft_text TEXT NOT NULL,
  scores JSONB NOT NULL,              -- {reply_spark: 7, conversation_depth: 5, ...}
  weighted_total DECIMAL(5,2),
  improvements JSONB,                 -- ["improvement 1", "improvement 2", ...]
  alternatives JSONB,                 -- ["alt version 1", "alt version 2"]
  final_posted_text TEXT,             -- what actually got posted (if any)
  final_post_id TEXT,                 -- X post ID if posted
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Voice profile for operator
CREATE TABLE voice_profiles (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  profile_name TEXT DEFAULT 'default',
  top_posts JSONB NOT NULL,           -- array of 10-20 best performing posts as few-shot examples
  style_notes TEXT,                   -- "direct, stoic, uses metaphors, never uses emojis"
  tone_keywords JSONB,                -- ["operator", "stoic", "leveraged", "no-fluff"]
  avoid_patterns JSONB,               -- ["exclamation marks", "emoji", "hashtags"]
  active BOOLEAN DEFAULT true,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Phase 2: Own post history (populated by Apify self-scrape)
-- CREATE TABLE own_posts ( ... );
-- Phase 2: Embeddings column on scraped_posts and own_posts
-- ALTER TABLE scraped_posts ADD COLUMN embedding vector(1536);
-- Phase 2: Content ideas
-- CREATE TABLE content_ideas ( ... );

8. Human Checkpoints (CRITICAL)

#TriggerWhat Human DoesBlocked Until
1Need Supabase projectGo to supabase.com → New Project → copy project URL and anon keySchema can be applied
2Need Apify account + API tokenGo to apify.com → Sign up → Settings → Integrations → Copy API tokenScraping workflow can call Apify
3Need X API Free tier appGo to developer.x.com → Create App → Generate OAuth 2.0 tokens (read+write)Reply posting workflow can call X API
4Need Telegram BotMessage @BotFather on Telegram → /newbot → copy bot token → get chat_idDigest workflow can send notifications
5Need Anthropic API keyGo to console.anthropic.com → API Keys → Create Key → copyLLM generation workflows can run
6Schema ready for SupabaseOpen Supabase SQL Editor → paste supabase-schema.sql → RunAll workflows can read/write data
7n8n credentials setupIn n8n: create credentials for Supabase, Apify, Anthropic, X API, TelegramWorkflows can be imported and activated
8Target accounts populatedInsert 15-20 target X accounts into Supabase target_accounts tableScraping workflow has targets
9Voice profile populatedInsert top 10-20 performing posts into voice_profiles tableReply generation uses operator voice
10End-to-end testManually trigger WF-01, verify scrape → replies → digest → post cycleSystem is production-ready

9. Execution Phases

1

Database & Accounts

Prerequisites: None. This is the first phase.

>>> HUMAN_ACTION_REQUIRED: Checkpoints 1-6 — Create accounts, get API keys, run SQL schema
Verification: Query SELECT table_name FROM information_schema.tables WHERE table_schema='public' returns 6 tables.
2

Scrape & Ingest Workflow (WF-01)

Prerequisites: Phase 1 complete. Human has completed Checkpoints 1-2, 6-7.

>>> HUMAN_ACTION_REQUIRED: Checkpoint 8 — Populate target accounts in Supabase
Verification: Manual trigger of WF-01 results in rows appearing in scraped_posts table.
3

Reply Generation Workflow (WF-02)

Prerequisites: Phase 2 complete. Checkpoint 5 (Anthropic key) done.

>>> HUMAN_ACTION_REQUIRED: Checkpoint 9 — Populate voice profile
Verification: Manual trigger results in reply_candidates rows with status='pending' for each new scraped post.
4

Digest & Approval Workflow (WF-03 + WF-04)

Prerequisites: Phase 3 complete. Checkpoint 4 (Telegram bot) done.

Verification: Receive Telegram digest, tap approve, verify reply appears on X.
5

Post Scorer Workflow (WF-05)

Prerequisites: Phase 1 complete (prompts + Anthropic key).

Verification: curl -X POST https://n8n.masterymade.com/webhook/score-post -H "Content-Type: application/json" -d '{"text":"test post"}' returns valid score JSON.
6

Performance Tracker (WF-06) + End-to-End Test

Prerequisites: Phases 2-5 complete. System has been running for 48+ hours.

>>> HUMAN_ACTION_REQUIRED: Checkpoint 10 — Run complete cycle manually and verify
Verification: Full cycle runs automatically for 7 days. Weekly digest received with meaningful metrics.

Future Extensions (NOT In Scope — Build Later)

PhaseFeatureTrigger to Build
Phase 2 60 DAYSContent RAG — pgvector embeddings on own post history, RAG-powered content coach chatAfter 200+ own posts with engagement data
Phase 2History Analyzer — Dashboard view of own posts sorted by engagement, topic clustersAfter Content RAG is live
Phase 3 90 DAYSBrain Dump tool — Free-form text → LLM generates posts, threads, article outlines in operator voiceAfter voice profile is validated by 60 days of reply data
Phase 3Content Composer — Idea storage CRUD + AI repurposing with voice profileAfter Brain Dump validates generation quality
Phase 4 MULTI-EXPERTChrome Extension — Save posts from X, repurpose in voiceAfter deploying for first expert (Brad)
Phase 4Multi-platform — LinkedIn, Threads, Bluesky adapters using same RAG pipelineAfter X-only version proves ROI
Phase 4White-label per expert — Supabase RLS, per-expert voice profiles, multi-tenantAfter 3+ experts onboarded
Phase 4FastAPI backend + Next.js frontend — Full SaaS UI replacing Telegram+Supabase dashboardAfter 10+ experts or direct customer demand

10. Target File Structure

contentengine/ supabase-schema.sql # Complete database schema prompts/ reply-gen.md # Reply generation system prompt post-scorer.md # Algorithm scoring system prompt voice-profile.md # Voice profile template + instructions weekly-analysis.md # Weekly performance analysis prompt workflows/ WF-01-scrape-ingest.json # n8n: Apify scrape → Supabase WF-02-reply-generation.json # n8n: New posts → LLM → Reply candidates WF-03-daily-digest.json # n8n: Pending replies → Telegram WF-04-post-replies.json # n8n: Telegram approve → X API post WF-05-post-scorer.json # n8n: Webhook → LLM score → JSON response WF-06-weekly-tracker.json # n8n: Performance metrics → Telegram digest docs/ CONTENTENGINE-PRD.html # This document CONTENTENGINE-GUIDE.html # Human setup guide CONTENTENGINE-ADR.html # Architecture decision record .env.template # Environment variable template

11. File Sync / Communication

Not applicable. No file sync required. All workflows imported directly into n8n via JSON import. SQL run directly in Supabase SQL Editor.

12. Environment Variable Template

# === CONTENTENGINE .env.template ===

# === SUPABASE ===
SUPABASE_URL=                  # https://xxxxx.supabase.co — from Supabase dashboard
SUPABASE_ANON_KEY=             # eyJ... — from Supabase Settings > API
SUPABASE_SERVICE_KEY=          # eyJ... — from Supabase Settings > API (service role)

# === APIFY ===
APIFY_TOKEN=                   # apify_api_xxxxx — from Apify Settings > Integrations

# === ANTHROPIC ===
ANTHROPIC_API_KEY=             # sk-ant-xxxxx — from console.anthropic.com

# === X / TWITTER ===
X_BEARER_TOKEN=                # AAAA... — from developer.x.com app
X_API_KEY=                     # Consumer key
X_API_SECRET=                  # Consumer secret
X_ACCESS_TOKEN=                # OAuth access token (user-level)
X_ACCESS_SECRET=               # OAuth access token secret

# === TELEGRAM ===
TELEGRAM_BOT_TOKEN=            # 123456:ABC-xxxxx — from @BotFather
TELEGRAM_CHAT_ID=              # Your personal chat ID — from @userinfobot

13. Cost Projections

13.1 Infrastructure Costs (Monthly)

ItemCost
n8n (existing instance)$0 (already running)
Supabase Free Tier$0
X API Free Tier$0
Telegram Bot$0
Infrastructure Total$0/mo

13.2 Variable Costs (Usage-Based)

ServiceUsageUnit CostMonthly Est.
Apify scraping~3,000 tweets/mo$0.40/1K tweets$1.20
Anthropic (reply gen)~4,500 calls/mo~$0.002/call$9.00
Anthropic (scoring)~150 calls/mo~$0.003/call$0.45
Anthropic (weekly digest)~4 calls/mo~$0.005/call$0.02
Variable Total~$10-12/mo

END OF PRD — Companion to CONTENTENGINE-GUIDE.html and CONTENTENGINE-ADR.html