architecture, pipelines, agents, and the rules each one follows. for those who want to understand what's behind the scenes.
Next.js at the center, Supabase for data and auth, external APIs for AI, images, and publishing.
Next.js 14 App Router
frontend + API routes + server components
Supabase
PostgreSQL + Auth + RLS + Storage
Gemini AI
text + image + review
Pexels + Imagen
real photos + AI backgrounds
WordPress
publishing + stats
GA4 + Google
analytics + web search
App Router with Server Components — pages that load data on the server without JS on the client
API Routes in the same project — no need for a separate backend
Instant deployment on Vercel with edge functions and cron jobs
Real PostgreSQL with Row Level Security — each organization only sees their own data
Built-in auth with sessions and automatic refresh tokens
Generous free tier to start — easy migration if you grow
6 steps. each one with its own agent. the article goes through all of them before being saved.
The system assembles everything it needs before writing.
If 'current' mode → performs web search on Google (up to 8 results) and filters by destination relevance
Searches for internal links and recent posts from the client's WordPress (for interlinking)
Loads the organization's brand profile (tone, audience, CTAs, forbidden terms)
Searches for performance insights (which angles and tones worked better in the past)
Automatically picks a content angle that hasn't been used recently
All context is packaged into one giant, structured prompt.
Entity (destination) + angle + tone + content mode
Web search results formatted with snippets
Internal WordPress links for Gemini to do interlinking
Brand rules: CTAs, forbidden terms, voice tone
Performance feedback: if 'storytelling' works better, the prompt tells Gemini that
User preferences: writing style, length, CTA style
Gemini Flash generates the complete article in structured JSON.
Model: gemini-3-flash-preview (fast + cheap for long articles)
Output: JSON with title, slug, excerpt, html, meta_title, meta_description, seo_keywords
Temperature 0.85 — creative but controlled
Max 16,384 tokens — room for 3,000+ word articles
System instruction defines rules: PT-PT, HTML formatting, no Brazilian Portuguese
A second model evaluates the article across 4 dimensions with different weights.
Authenticity (40%) — does it sound like a real person? has its own voice? not generic?
Factual Accuracy (30%) — are facts verifiable? are prices plausible? are names correct?
SEO (20%) — keyword in title? meta description? H2s with variation?
Engagement (10%) — hooks in paragraphs? natural CTAs? does it flow well?
Final score = weighted average. If < 70 → triggers automatic rewrite
Each dimension gets a score 0-10 + specific feedback
Issues classified as: critical / warning / suggestion
If the score is low, the article is rewritten with feedback from the rubric.
Maximum 2 rewrite attempts — doesn't get stuck in an infinite loop
The rewrite prompt includes: original article + rubric feedback + worst dimension
Focuses on the weakest dimension (ex: if 'authenticity' failed, asks to add personal opinion)
After each rewrite, it goes through the rubric again — can stop before 2 if score > 70
Rewrite history is saved for analysis
Normalization, images, and sanitization before saving.
Normalizes PT-BR → PT-PT (Brazilian Portuguese → European Portuguese)
Corrects future years if the topic didn't specify a year
Replaces image placeholders with real photos from Pexels (with relevance scoring)
Automatically removes links to competitor sites (Booking, Expedia, etc.)
Generates social content (Instagram + TikTok) from the article
Saves everything to Supabase: article + images + metrics + usage logs
the article is analyzed, the best items are extracted, and visual creatives are generated automatically.
Gemini analyzes the HTML and classifies the article type.
Types: listicle, guide, destination, comparison, seasonal, experience
Extracts individual items: name, description, image search term (in English for Pexels)
Confidence: percentage certainty in classification (ex: 98% guide)
Output: structured JSON via responseMimeType: application/json
Automatic rules define how many slides to show and what to include.
Listicle with 10+ items → shows 5 + CTA slide 'see the rest on the website'
Guide with 5 items → shows all + cover + CTA
Destination → hero cover + 3-4 highlights + CTA
Each type has a recommended template, number of slides, and different CTA text
These are 'business rules' — logic that doesn't depend on AI, lives in code
Smart interface guides the user through configuration.
Step 1: Choose template (with recommendation based on article type)
Step 2: Select items to include (toggle pills — can add/remove)
Step 3: Accent color + formats (Story/Post) + final actions
Pattern: Progressive Disclosure — shows the essentials, hides complexity
Fallback system to find the best possible image.
1st: Pexels API — searches images with relevance scoring (location match + entity match)
If score < threshold → 2nd: Imagen 4 (Google) generates photorealistic background
Imagen prompt is built with content type, shot style, and negative restrictions
Final fallback: color gradient as emergency background
Imagen cost: ~$0.03/image — only used when Pexels has nothing good
JSX templates are converted to images without a browser.
Satori converts JSX → SVG directly on the server (edge-compatible)
External images are pre-converted to base64 (Satori can't resolve HTTP URLs)
Poppins fonts loaded as buffers for consistent text rendering
Output: high-res PNG (1080×1920 Stories, 1080×1080 Posts)
Everything happens in an API Route — zero browser or puppeteer dependency
it's not one model doing everything. there are several specialized agents, each with specific rules.
Writes the complete article
gemini-3-flash
Always PT-PT, never PT-BR
Semantic HTML with H2/H3 hierarchy
Minimum 3 internal links from the client's WordPress
Natural CTAs (not 'click here')
FAQ schema-ready at the end
Varies tone between articles (doesn't repeat)
Evaluates quality with 4D rubric
gemini-3-flash
Temperature 0.3 — consistent and objective
4 dimensions with weights: Auth 40%, Facts 30%, SEO 20%, Engage 10%
Score < 70 → rewrite mandatory
Identifies 'worst dimension' to focus the rewrite
Classifies issues: critical / warning / suggestion
Maximum 2 rewrites to avoid loops
Classifies article and extracts items
gemini-3-flash
6 recognized types: listicle, guide, destination, comparison, seasonal, experience
Structured Output — JSON mandatory, no free text
Extracts: position, title, description, imageSearchTerm
imageSearchTerm always in English (for Pexels API)
Minimum confidence reported for each classification
Scores Pexels image relevance
code (not AI)
Location matching: destination name in photo alt/description
Entity matching: if the photo mentions the specific location
Deduplicates: doesn't use the same image twice in one article
Configurable minimum score (higher for campaigns)
Falls back to Imagen 4 if no photo passes the threshold
Converts PT-BR → PT-PT
code (not AI)
Automatically replaces common Brazilian Portuguese terms
Corrects future years in 'current' content
Generates safe slug from title
Removes links to competitor sites (Booking, Expedia, etc.)
List of 14 blocked competitor domains
Real feedback to improve future articles
code + GA4/Jetpack
Automatic sync every 6h via Vercel Cron
GA4 first, Jetpack as fallback (or mixed)
Calculates: which tone, angle, style worked better
Injects insights into Writer Agent prompt
Dashboard shows real KPIs: views, visitors, trends
Try A → if it fails → B → if it fails → C. Used in images (Pexels → Imagen → gradient), login (client → server → error), and performance sync (GA4 → Jetpack).
Force Gemini to return JSON with specific fields via responseMimeType. Eliminates free-text parsing and guarantees data that code can use.
Show the essentials first, reveal details on request. Used in GuidedCreativeMode (3 steps) and campaign configuration.
I'm not a developer. I'm a Google Ads guy who decided to learn to build. here's what I learned along the way.
A friend has a travel agency (Norte Viagens, ~€1M/year). Needed content for the website. No team, no time, no budget for a marketing agency.
What if AI could generate blog articles that actually brought traffic? Not generic garbage — real content, in European Portuguese, with real SEO, relevant images, and automatic publishing.
Zero experience in Next.js, TypeScript, or Supabase. Started with Claude and ChatGPT as co-pilots. Every session was: build a feature + understand the 'why' behind it. Pure project-based learning.
Discovered that 80% of output quality is in the prompt. Structured output, system instructions with negative constraints, few-shot examples, temperature tuning. It's not magic — it's engineering.
The 4D rubric was born when I realized AI without review generates garbage 30% of the time. Authenticity (40%) + Facts (30%) + SEO (20%) + Engagement (10%). Auto-rewrite if score < 70. Quality jumped dramatically.
Real performance (GA4 + WordPress) feeds into future prompts. If articles with 'storytelling' tone have more views, the system adapts automatically. It's not static — it learns.
practical lessons from someone who doesn't come from computer science.
Force JSON with specific fields eliminates 90% of parsing problems. Using responseMimeType: 'application/json' in Gemini changes everything.
One model doing everything = meh results. Splitting into Writer, Reviewer, Analyzer, each with different temperature and instructions = consistent results.
Never depend on a single API. Pexels failed? Imagen. Client login failed? Try server. GA4 with no data? Jetpack. Always have a plan B and C.
AI without review generates acceptable content 70% of the time. With review + auto-rewrite, it goes up to 95%+. The extra token cost pays off.
The more context you put in the prompt (brand profile, performance data, previous posts, internal links), the better the output. Generic prompts = generic results.
Perfection is the enemy of shipping. Started with basic articles without review. Then added rubric, then creatives, then performance loop. Each sprint made the product better.
Claude and ChatGPT are brutal for coding when you ask 'why' not just 'how'. Learned more TypeScript in 3 months of building than in any course.
AI doesn't replace taste, strategy, or business knowledge. But it automates the 8 hours of repetitive work that nobody wants to do.