Plataforma / AI Ops
Cómo construí esta plataforma — con AI, desde cero, sin un solo desarrollador
Lo que estás leyendo es el resultado
Esta plataforma — la que estás usando ahora mismo — no fue construida por un equipo de desarrollo. No hubo un CTO, no hubo un freelancer en Upwork, no hubo un sprint de dos semanas.
La construyó un agente de AI llamado Cori, operado por mí a través de WhatsApp, en una sola noche.
Este es el log completo. Cada paso, cada decisión, cada error, cada comando que se ejecutó. Sin editar.
El punto de partida
Herramienta: OpenClaw — un sistema operativo de AI personal que corre en mi Mac mini en casa, disponible 24/7, que recibe instrucciones por WhatsApp y ejecuta tareas reales en mi computadora.
Punto de partida: Un dominio comprado (mo-insider.com), una idea de lo que quería construir, y un agente con acceso a terminal.
Lo que quería: Una plataforma de membresía con:
- Registro con aprobación manual (yo apruebo quién entra)
- Cursos con contenido estructurado
- Comunidad (foro interno)
- Newsletter desde el propio dominio
- Panel de admin para gestionar miembros
Tiempo total: Una noche (aproximadamente 6 horas de trabajo real del agente)
Fase 1 — Arquitectura y stack
Lo primero fue decidir el stack. Le pregunté a Cori qué tenía sentido para lo que quería construir. La respuesta:
Stack recomendado:
- Next.js 14 (App Router) — frontend + API routes
- Supabase — base de datos PostgreSQL + autenticación
- Resend — emails transaccionales
- Vercel — deployment y hosting
- TypeScript — para no morir en errores de runtime
Razones concretas: Supabase tiene Row Level Security (RLS) nativo — esencial para que los miembros solo vean lo que deben ver. Vercel se integra con Supabase automáticamente. Resend es el servicio de email más limpio del mercado para developers. Next.js 14 permite tener backend y frontend en el mismo proyecto.
Costo mensual estimado del stack: $0–20 USD/mes en el nivel gratuito de todas las plataformas.
Fase 2 — Build: 26 archivos, cero errores de compilación
Cori construyó el proyecto completo en una sola pasada. 26 archivos, todos funcionales:
Autenticación y acceso:
middleware.ts— protege /cursos, /comunidad, /admin. Si no estás autenticado o aprobado, te redirige al loginapp/login/page.tsx— página de login conectada a Supabase Authlib/supabase.ts— cliente de Supabase con inicialización lazy (importante: esto se corrigió después, más abajo)
Registro y aprobación:
components/JoinModal.tsx— formulario de 7 campos: nombre, apellido, empresa, sector, uso actual de AI, objetivos, cómo llegasteapp/api/notify-admin/route.ts— cuando alguien aplica, me llega una notificaciónapp/api/approve-member/route.ts— apruebo desde el panel, el sistema manda email de bienvenidaapp/api/send-newsletter/route.ts— newsletter con personalización{{nombre}}
Panel de admin (/admin):
- Tab 1: solicitudes pendientes de aprobación
- Tab 2: miembros activos (con opción de suspender)
- Tab 3: compositor de newsletter
Comunidad (/comunidad):
- Posts y respuestas conectados a Supabase en tiempo real
- Políticas RLS: solo miembros aprobados pueden postear
Cursos (/cursos):
- Estructura modular, protegida por auth
- Curso 1 y Curso 2 con sus respectivos módulos
Total: 140 archivos en el commit final (incluyendo dependencias configuradas, assets, schema SQL, vercel.json)
Fase 3 — Base de datos: el schema SQL
Cori escribió el schema completo para Supabase. Esto fue lo que se ejecutó en el SQL Editor de Supabase:
-- Tabla principal de miembros
CREATE TABLE members (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
company TEXT NOT NULL,
industry TEXT NOT NULL,
industry_other TEXT,
ai_usage TEXT NOT NULL,
goals TEXT NOT NULL,
how_found TEXT NOT NULL,
referral_name TEXT,
status TEXT DEFAULT 'pending'
CHECK (status IN ('pending', 'approved', 'rejected', 'suspended')),
created_at TIMESTAMPTZ DEFAULT NOW(),
approved_at TIMESTAMPTZ,
approved_by TEXT
);
-- Tabla de posts de la comunidad
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
author_id UUID REFERENCES members(id) ON DELETE CASCADE,
title TEXT NOT NULL,
body TEXT NOT NULL,
category TEXT NOT NULL,
pinned BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Tabla de respuestas
CREATE TABLE replies (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
author_id UUID REFERENCES members(id) ON DELETE CASCADE,
body TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Vista para newsletter (solo miembros aprobados)
CREATE VIEW newsletter_list AS
SELECT id, email, first_name FROM members
WHERE status = 'approved';
Row Level Security — las políticas que controlan quién ve qué:
-- Habilitar RLS en todas las tablas
ALTER TABLE members ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE replies ENABLE ROW LEVEL SECURITY;
-- Cualquiera puede registrarse (insertar su propio registro)
CREATE POLICY "allow_registration" ON members
FOR INSERT WITH CHECK (auth.uid() = id);
-- Un miembro solo puede ver su propio perfil
CREATE POLICY "members_read_own" ON members
FOR SELECT USING (auth.uid() = id);
-- Solo miembros aprobados pueden ver y crear posts
CREATE POLICY "approved_members_posts" ON posts
FOR ALL USING (
EXISTS (
SELECT 1 FROM members
WHERE id = auth.uid() AND status = 'approved'
)
);
Cómo se ejecutó: Supabase tiene un SQL Editor en el dashboard. Cori abrió el browser, navegó a supabase.com, abrió el SQL Editor del proyecto, pegó el schema completo, y presionó Run. Resultado: "No rows returned" — lo que significa éxito (el schema no devuelve datos, solo crea estructura).
Fase 4 — Deployment en Vercel
Problema 1: sin repositorio GitHub
La cuenta cori@gruponc44.com no tiene GitHub. La solución: Vercel CLI, que permite hacer deploy directo desde terminal sin repositorio.
# Desde el directorio del proyecto
cd /Users/ceo-nc44/the-mo-app
# Deploy a producción
vercel --prod --token [TOKEN]
El token se genera en vercel.com/account/tokens. Cori abrió el browser, navegó al dashboard de Vercel, generó el token, y lo usó en el comando.
Primer deploy: falló. Error: RESEND_API_KEY is not defined
El cliente de Resend se inicializaba al cargar el módulo (antes de que las variables de entorno estuvieran disponibles). Fix: inicialización lazy en las tres rutas afectadas.
// ❌ Antes — se inicializa al importar el módulo
const resend = new Resend(process.env.RESEND_API_KEY);
// ✅ Después — se inicializa solo cuando se llama la función
function getResend() {
return new Resend(process.env.RESEND_API_KEY);
}
Segundo deploy: compiló. Pero error en browser: supabaseKey is required
El bundle de Next.js había compilado el cliente de Supabase con la variable NEXT_PUBLIC_SUPABASE_ANON_KEY vacía (se había cargado mal en Vercel). El hash del chunk JS estaba cacheado. Fix: mismo patrón de lazy initialization para el cliente de Supabase, forzando regeneración del bundle.
// lib/supabase.ts — versión final
import { createClient } from "@supabase/supabase-js";
let _client: ReturnType<typeof createClient> | null = null;
export function getSupabaseClient() {
if (!_client) {
_client = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
return _client;
}
Tercer deploy: éxito. 26 páginas generadas, cero errores.
🟢 https://the-mo-app.vercel.app — live.
Fase 5 — Dominio propio y DNS
Dominio: mo-insider.com, comprado en Namecheap.
Paso 1 — Agregar dominio a Vercel:
En el dashboard de Vercel → proyecto → Settings → Domains → agregar mo-insider.com.
Vercel provee los registros DNS necesarios:
A Record:@→76.76.21.21CNAME Record:www→cname.vercel-dns.com
Paso 2 — Configurar en Namecheap Advanced DNS:
Nota real del proceso: el CNAME tenía un typo — decía came.vercel-dns.com en lugar de cname.vercel-dns.com. Se detectó revisando el screenshot que me mandé a mí mismo y se corrigió antes de guardar.
Resultado: SSL auto-provisionado por Vercel. mo-insider.com activo en ~10 minutos.
Fase 6 — Email transaccional con Resend
Resend es el servicio de email para developers. Permite enviar emails transaccionales (bienvenidas, aprobaciones, newsletters) desde tu propio dominio con excelente deliverability.
Pasos ejecutados:
1. Crear cuenta en resend.com con cori@gruponc44.com
2. Confirmar cuenta — llegó un email de confirmación. Cori abrió el link desde el inbox de Gmail usando gog (Google Workspace CLI):
gog gmail search "from:resend" --account cori@gruponc44.com
# → encontró el email con el link de confirmación
# → Cori abrió el link en el browser y presionó "Confirm account"
3. API Key generada: re_6MJBqoap_... — agregada a Vercel como variable de entorno:
vercel env add RESEND_API_KEY production --token [TOKEN]
# → prompts por el valor → se ingresa la key
# → "Added Environment Variable RESEND_API_KEY to Project the-mo-app"
4. Verificar dominio mo-insider.com en Resend:
Resend requiere registros DNS para confirmar que eres el dueño del dominio y para mejorar la deliverability.
Registros DNS agregados en Namecheap:
| Tipo | Host | Valor | Para qué sirve |
|---|---|---|---|
| TXT | resend._domainkey |
p=MIGfMA0G... |
DKIM — firma digital de los emails |
| TXT | send |
v=spf1 include:amazonses.com ~all |
SPF — autoriza a Amazon SES a enviar en nombre del dominio |
DKIM (DomainKeys Identified Mail): Firma criptográfica que prueba que el email no fue alterado en tránsito. Sin esto, los emails pueden terminar en spam.
SPF (Sender Policy Framework): Lista de servidores autorizados a enviar emails desde tu dominio. Resend usa la infraestructura de Amazon SES, por eso se incluye amazonses.com.
Estado: DNS propagando (Namecheap puede tardar hasta 2 horas). Una vez verificado, los emails salen desde hola@mo-insider.com.
Fase 7 — Usuario de prueba (cómo se creó mi acceso)
No quería pasar por el flujo de aprobación para testear la plataforma. Cori creó mi usuario directamente via la API de Supabase:
Paso 1 — Crear usuario en Supabase Auth:
curl -X POST "https://[proyecto].supabase.co/auth/v1/admin/users" \
-H "apikey: [SERVICE_ROLE_KEY]" \
-H "Authorization: Bearer [SERVICE_ROLE_KEY]" \
-H "Content-Type: application/json" \
-d '{
"email": "michel.olmi@gmail.com",
"password": "...",
"email_confirm": true
}'
# → "id": "2b39bd21-...", "email": "michel.olmi@gmail.com"
Paso 2 — Insertar en tabla members como approved:
curl -X POST "https://[proyecto].supabase.co/rest/v1/members" \
-H "apikey: [SERVICE_ROLE_KEY]" \
-H "Authorization: Bearer [SERVICE_ROLE_KEY]" \
-H "Content-Type: application/json" \
-d '{
"id": "2b39bd21-...",
"email": "michel.olmi@gmail.com",
"first_name": "Michel",
"last_name": "Olmi",
"company": "RiTMO",
"industry": "Retail",
"ai_usage": "A diario personal",
"goals": "Acceso completo",
"how_found": "Referido",
"status": "approved"
}'
Resultado: acceso completo a cursos, comunidad y admin.
Lo que esto demuestra
Tiempo total: ~6 horas (una noche)
Costo de desarrollo: $0 (el agente ya estaba operando)
Costo mensual del stack:
| Servicio | Plan | Costo |
|---|---|---|
| Vercel | Hobby (gratuito) | $0 |
| Supabase | Free tier | $0 |
| Resend | Free (3,000 emails/mes) | $0 |
| Namecheap (dominio) | Anual | ~$15/año |
| OpenClaw | Suscripción mensual | $30/mes |
Total operativo: ~$30/mes para una plataforma completa con auth, base de datos, emails, comunidad, cursos y panel de admin.
La pregunta que importa
¿Podría haber contratado a un desarrollador para hacer esto?
Sí. Costo estimado: $3,000–8,000 USD para un proyecto de esta envergadura, más 2–4 semanas de tiempo.
¿Vale la pena aprender a operar un agente de AI para este tipo de trabajo?
Depende de cuántas veces necesites construir algo. Si es una vez, quizás no. Si construyes sistemas continuamente — plataformas, automatizaciones, herramientas internas — la respuesta es sí, con mucho margen.
No se trata de reemplazar developers. Se trata de poder ejecutar sin depender de ellos para proyectos donde la velocidad y el costo importan.
El stack completo para replicarlo
Si quisieras construir algo similar:
- OpenClaw — instalación en Mac o VPS, conexión por WhatsApp
- Node.js + Next.js 14 —
npx create-next-app@latest --typescript - Supabase — crear proyecto en supabase.com, copiar URL y keys
- Vercel — conectar proyecto o usar CLI (
npm i -g vercel) - Resend — crear cuenta, verificar dominio, obtener API key
- Namecheap (o cualquier registrar) — configurar DNS records
El agente hace el resto. Lo que necesitas es saber qué quieres construir — no cómo construirlo.