STORYPRINTS — Un cuento que se imprime y se colorea
Ocho respuestas de un padre convertidas en un objeto físico en dos minutos · SaaS · B2C · EdTech
- Cliente
- Side project — producto propio de RBT Studio
- Rol
- Producto, diseño y desarrollo full stack (una sola persona)
- Periodo
- Marzo 2026 – en curso
- Stack
- Next.js 15, React 18, TypeScript, Supabase, Claude, OpenRouter, Upstash Redis + BullMQ, pdfkit, Lemon Squeezy
El hueco estaba entre el libro por encargo y la ventana de chat
Un libro para colorear de tienda es genérico por definición: el protagonista nunca es tu hija. Las alternativas personalizadas son libros impresos por encargo — caros, con semanas de espera y una sola tirada. En el otro extremo, un chat de IA puede escribirte un cuento, pero devuelve texto en una ventana: no es un libro, no tiene portada, no se imprime, no se colorea, no se queda en la estantería.
El problema: Tres restricciones que decidieron el diseño técnico
El artefacto final es un PDF A4, no una pantalla: todo lo que se genera tiene que sobrevivir a una impresora doméstica. El público son niños de 3 a 12 años, así que contenido que no ha vetado nadie no puede llegar a una página impresa. Y cada libro cuesta dinero real en llamadas a modelos — la economía del producto tenía que estar en el código desde el principio, no como una capa añadida después.
Cómo se abordó
- El wizard: ocho preguntas, no un prompt. La primera decisión de producto fue no exponer un campo de texto libre. Un padre no quiere escribir un prompt; quiere contestar preguntas con su hija al lado. Nombre, edad, escenario, acompañante, valor, identidad, extensión y estilo narrativo — cada respuesta es una tarjeta. Ese StoryConfig validado es el único contrato que entra en el pipeline: nada aguas abajo vuelve a preguntarle nada al navegador.
- Una cola, y un cron que la rescata. Las generaciones tardan minutos y cuestan dinero, así que van a una cola (BullMQ sobre Upstash Redis) con progreso hacia el navegador. Se drena por dos vías: en proceso justo después de responder, y un cron cada 5 minutos. El cron no es redundancia decorativa — es lo único que hace que una cola atascada se arregle sola. El endpoint falla cerrado si no hay CRON_SECRET, porque cada llamada gasta dinero en modelos.
- Las ilustraciones no pueden llevar texto. Son páginas para colorear: una letra horneada en el bitmap no se quita, y la página se imprime tal cual. El modelo de imagen se llama por chat completion, sin negative prompt, así que todo viaja dentro del texto — la instrucción se repite de tres formas distintas y el diálogo entrecomillado se elimina de la escena antes de que el modelo la vea, porque las comillas son la señal más fuerte para que un modelo dibuje letras. Es mitigación a nivel de prompt, no una garantía, y la documentación lo dice así.
- La economía va en el código. Cada imagen se paga en créditos, y el plan compra una asignación mensual que caduca al cerrar el periodo: una asignación que se acumula no es una asignación. Durante un tiempo el webhook de suscripción escribía el plan y nada más, así que un suscriptor tenía cero créditos y recibía los mismos dibujos genéricos que el plan gratuito. Hoy grantPlanCredits() corre en la creación, la actualización y cada pago mensual — idempotente por descripción, con un índice único en Postgres que lo hace cumplir de verdad.
- Dos decisiones de negocio deliberadas. El plan gratuito entrega un libro completo e imprimible: retener el PDF dejaría al plan gratis sin nada por lo que juzgar el producto — lo que quita la suscripción es la marca de agua. Y el primer cuento de cada usuario lleva ilustración de IA de verdad, sea cual sea su plan; si no, el único libro por el que un visitante juzga el producto se renderiza con seis SVG genéricos, es decir, sin nada de la personalización por la que se le está pidiendo pagar. Un registro cuesta una llamada de imagen: es publicidad barata.
wizard → PDF: Wizard · 8 pasos → StoryConfig validado → Cola BullMQ · Redis → Narrative Engine · Claude → Content Safety → Illustration Engine · OpenRouter → Supabase Storage → Lector SVG → PDF A4 bajo demanda
Solución: Un SaaS completo, de la landing al PDF, construido y operado por una persona
Wizard de ocho pasos con subida opcional de una foto del niño, con consentimiento explícito y nunca escrita en la columna config del cuento. Motor narrativo sobre Claude con escenas ajustadas a la edad y extensiones de 6 a 18 páginas derivadas del número de escenas. Motor de ilustración sobre OpenRouter, línea negra sobre blanco y coherente entre escenas. Filtro de contenido aplicado tanto a lo que devuelve el modelo como al único campo libre del producto. Lector web en SVG y exportación a PDF con pdfkit. Cuatro planes leídos desde una única tabla de límites, share card propia, panel de admin y suscripciones con Lemon Squeezy. El PDF no está en el pipeline: se compone bajo demanda desde el cuento ya guardado, para que una generación nunca se bloquee esperando a la maquetación.
Resultados
El producto está construido y desplegado, y aún no está validado en mercado. No hay métricas de usuarios, conversión ni ingresos que reportar, y prefiero no inventarlas. Lo que sí se puede afirmar: el circuito completo funciona en producción, de las ocho respuestas del wizard al PDF descargable, con pagos y suscripciones activas. Los invariantes del negocio están bloqueados por tests, no por disciplina — la tabla de límites y la página de precios no pueden divergir porque hay un test que falla si lo hacen. Y la cola se autocorrige: una generación cuyo disparo en proceso se interrumpe la recoge el cron en los siguientes 5 minutos. Lo que queda por validar es lo importante: si un padre paga 6,99 € al mes por esto.
- 376 — tests en 30 suites, todos en verde
- 113 — commits en cinco meses de trabajo de una sola persona
- 4 — planes leídos desde una única tabla de límites
"Un flag que ninguna ruta lee no es un límite: es un comentario que lo parece."
Qué se aprendió
- Un flag que ninguna ruta lee no es un límite. La tabla de planes existía mucho antes de que las rutas la consultaran. El bug de los créditos —un suscriptor recibiendo los mismos dibujos que el plan gratis— no fue un error de código: fue una configuración que nadie ejecutaba. Ahora cada límite tiene una ruta que lo lee y un test que lo prueba.
- Lo que no se puede quitar después, se impide antes. Una letra horneada en un bitmap que se va a imprimir no tiene arreglo posterior, así que la mitigación tiene que vivir en el prompt y en el preprocesado de la escena — y documentarse como mitigación, no como garantía.
- Sacar el PDF del pipeline fue lo que hizo el sistema robusto. Componer bajo demanda desde el cuento guardado significa que un fallo de maquetación nunca bloquea una generación ya pagada.
Producto en producción, pendiente de validación en mercado.