{"slug": "my-build-in-public-journey-creating-an-ebook-generator", "title": "My build in public journey: creating an Ebook generator", "summary": "A developer built Ebookr.ai, an AI-powered ebook generator that creates a complete ebook in about 15 minutes, replacing a tedious multi-tool workflow. The system uses a multi-agent pipeline with six specialized agents, a custom pagination engine, and a marketing agent that audits ad spend and provides daily reports. The developer also discovered significant tracking issues in their ad accounts through the marketing agent's cross-checking.", "body_md": "I once spent three weekends writing a single ebook for my Python course. Last month, my product ([Ebookr.ai](https://ebookr.ai)) generated a better one in about fifteen minutes, while I was making coffee. This is the story of the gap between those two sentences.\n\nI run [Jornada Python](https://pythonacademy.com.br/jornada-python/), a Python course for Brazilian devs. Ebooks are great assets for a course business: lead magnets, bonus material, complementary content. Students love them.\n\nThe problem: I hated making them.\n\nWriting was slow. Formatting was slower. And I am the kind of person who notices when a heading is 2px off, which turns \"quick formatting pass\" into a lost evening.\n\nWhen ChatGPT showed up, I thought I was saved. Narrator voice: he was not. 🙃\n\nThe text got faster, sure. But an ebook is not text. It is text plus a cover plus images plus infographics plus layout plus a PDF that does not break in hilarious ways. So my workflow became: generate text in one tab, make a cover in another tool, hunt stock images in a third, assemble everything in a design tool, export, find a table sliced in half between pages, cry, repeat. The result of all that gluing looked like a ransom note with chapters.\n\nThat pain felt very automatable. So I built [Ebookr.ai](https://ebookr.ai).\n\nThe core is a multi-agent pipeline. Instead of one giant prompt praying for a miracle, there are six specialized agents, each with one job:\n\nThe PDF part is where I lost some hair. My first version used Markdown to Pandoc to WeasyPrint, which works right up until you want magazine-style layouts. The current version renders Jinja2 templates in headless Chromium via Playwright, with a custom pagination engine deciding where pages break. Yes, I wrote a pagination engine in 2026. No, I do not want to talk about it. (I do. Ask me anything.)\n\nBoring on purpose: Django 5 and Python 3.11, Celery with Redis for all the heavy async work, Postgres with pgvector, LangChain and LangGraph orchestrating the agents, OpenAI models behind it, Playwright for PDF rendering, Stripe for billing, Cloudflare R2 for storage.\n\nThe most exotic thing here is the agent orchestration. Everything else is the stack you would pick for a normal SaaS, because 90% of an AI product is a normal SaaS.\n\nI build almost everything using Claude Code sessions. Not as an autocomplete, more like a colleague with unlimited patience: it reads the codebase, follows the project's rule file, writes the tests, and pushes back when I ask for something that contradicts a business rule we defined earlier. Being corrected by your own tooling is a strange kind of pride.\n\nThe workflow that emerged: I describe intent and constraints, Claude proposes and implements, tests gate everything, and I review like a slightly paranoid tech lead. Solo founder, but it rarely feels solo.\n\nThis is my favorite part. I am a developer; ads dashboards scare me more than segfaults. So I set up a dedicated marketing agent: its own instructions file, its own subagents (data analyst, CRO specialist, copywriter), and read-only access to everything that matters: Google Ads, GA4, Search Console, Microsoft Clarity and Stripe through MCP connectors, plus the production database through an SSH tunnel with a read-only role.\n\nEvery day it cross-checks ad spend against real signups and real revenue (never trusting the ad platform's own numbers), and ends every report with three prioritized actions, what NOT to do, and what data is missing.\n\nWhy an agent instead of an agency? Because I hired an agency first. What the agent found while auditing my accounts deserves its own horror anthology 🕵️:\n\nThe agent surfaced all of this in one afternoon of cross-checking, then we rebuilt the tracking from scratch. The agency is gone. The agent stays.\n\nGuardrails matter: it can read everything, it can change nothing. Every mutation goes through me. An autonomous agent with write access to your ad account is a horror movie pitch.\n\nBuild in public without numbers is just marketing, so here goes:\n\nTiny numbers. But they are mine, they are real, and the unit economics work. The current battle is conversion, not the product, and that battle is being fought with session recordings, funnel instrumentation and a lot of stubbornness.\n\nThe product is Portuguese-only today; the English version is fully built and waiting behind a feature flag while I finish the boring parts (legal review, USD pricing). If you read this far and want to see it anyway: [ebookr.ai](https://ebookr.ai). The pricing page is in Portuguese, but numbers are numbers. 😄", "url": "https://wpnews.pro/news/my-build-in-public-journey-creating-an-ebook-generator", "canonical_source": "https://dev.to/viniciusramos91/my-build-in-public-journey-creating-an-ebook-generator-hh0", "published_at": "2026-07-17 22:46:40+00:00", "updated_at": "2026-07-17 23:29:58.863160+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "developer-tools", "ai-products", "generative-ai"], "entities": ["Ebookr.ai", "Jornada Python", "OpenAI", "LangChain", "LangGraph", "Claude Code", "Playwright", "Stripe"], "alternates": {"html": "https://wpnews.pro/news/my-build-in-public-journey-creating-an-ebook-generator", "markdown": "https://wpnews.pro/news/my-build-in-public-journey-creating-an-ebook-generator.md", "text": "https://wpnews.pro/news/my-build-in-public-journey-creating-an-ebook-generator.txt", "jsonld": "https://wpnews.pro/news/my-build-in-public-journey-creating-an-ebook-generator.jsonld"}}