Bloom is a production-style SaaS web app for marketers to create, improve, save, and revisit AI-generated campaign content. It supports multi-format text generation, matching image generation per post, session-scoped history, saved visual versions, copy/download/export actions, and brand voice defaults.
Built for the Magna Labs 48-Hour AI Engineering Challenge.
| Requirement | Status |
|---|---|
| AI content generator | Supports blog posts, LinkedIn posts, ad copy, and newsletters with distinct prompt strategies. |
| AI image generator per post | Generates a visual companion from saved content context and selected style. |
| Regenerate image with style | Regeneration creates a new prompt/context-based image version without image-to-image dependency. |
| Content history and dashboard | Saves generated text and images per browser session, with dashboard, library, detail view, copy, download, and delete. |
| AI content improver | Supports shorter, persuasive, formal, SEO, and audience rewrite goals with explanation. |
| REST API backend | All AI and storage flows use Next.js API routes; frontend never calls AI providers directly. |
| README API docs | Endpoints, request shapes, errors, setup, architecture, and deployment notes are documented here. |
| Bonus features | Brand voice settings, image style picker, export/print flow, polished shadCN/Tailwind UI. |
- Next.js App Router
- React
- TypeScript
- Tailwind CSS
- shadCN-style UI primitives
- Server-side REST API routes
- File-backed JSON persistence for assessment/demo use
- OpenAI-compatible text provider support
- Google image generation support
- Mock fallback when provider keys are missing
npm install
npm run devOpen:
http://localhost:3000
For the production build:
npm run build
npm run startCreate .env.local in the project root. Do not commit this file.
# Required for signed session cookies in any shared/deployed environment
SESSION_SECRET=replace_with_a_long_random_secret
# Local HTTP only. Use true on HTTPS deployments.
BLOOM_SECURE_COOKIES=falseBloom supports OpenAI-compatible chat completions for text generation:
AI_TEXT_BASE_URL=https://provider.example.com/v1
AI_TEXT_MODEL=mimo-v2.5-pro
AI_TEXT_API_KEY=provider_key_here
AI_TEXT_REASONING_EFFORT=low
AI_TEXT_DISABLE_THINKING=trueIf using OpenAI directly instead:
OPENAI_API_KEY=your_openai_key
OPENAI_TEXT_MODEL=gpt-4.1-miniFor Google image generation:
GOOGLE_AI_API_KEY=your_google_ai_key
IMAGE_MODEL=imagen-4.0-generate-001If no text or image key is configured, Bloom uses mock generation so the app remains demoable. API responses include provider metadata such as mock, xiaomi, openai, google, or custom.
For deployed persistence, set a Postgres connection string:
DATABASE_URL=postgresql://user:password@host/database?sslmode=requireWhen DATABASE_URL is present, Bloom automatically creates the required tables and stores session history, image versions, and settings in Postgres. When it is absent, Bloom falls back to local file storage in .bloom-data/content-store.json.
Bloom keeps the backend inside the Next.js app using App Router route handlers.
Client UI
-> /api/content/*
-> /api/settings
-> session cookie
-> validation/rate limit helpers
-> AI prompt builders
-> provider adapters
-> file-backed storage
Important modules:
src/lib/backend/session.ts- creates and verifies the signed opaque session cookie.src/lib/backend/storage.ts- stores content, image versions, and settings in Postgres whenDATABASE_URLexists, otherwise in.bloom-data/content-store.json.src/lib/backend/api.ts- shared validation, error responses, parsing, and rate-limit helpers.src/lib/backend/ai/prompts.ts- centralized prompt strategies, image prompt rules, style prompts, and quality rubric.src/lib/backend/ai/text.ts- server-side text provider calls and mock fallback.src/lib/backend/ai/image.ts- server-side image provider calls and mock fallback.src/lib/content-client.ts- browser-side REST client wrapper.
All responses use JSON. Successful responses include success: true. Errors use the format documented below.
Generates and saves a new content record.
Request:
{
"topic": "Sustainable living tips",
"audience": "Millennials interested in eco-friendly habits",
"tone": "Professional",
"contentType": "blog",
"brandVoice": "Warm, clear, and practical"
}Rules:
topic,audience, andtoneare required.contentTypemust beblog,linkedin,ad, oremail.brandVoiceis optional.
Lists saved content for the current session.
Query params:
type:all,blog,linkedin,ad, oremailsearch: text search across title, excerpt, and contentpage: page numberlimit: items per page
Returns one saved content item if it belongs to the current session.
Deletes a saved content item and its image versions.
Generates a visual companion for a saved content item.
Request:
{
"style": "photographic"
}style must be one of:
photographicillustration3dminimalistabstract
Creates the next image version for the same saved content item. Regeneration reuses saved text context and the selected style, but does not pass the previous image back to the model.
Improves existing copy and returns a short explanation.
Request:
{
"content": "Paste existing copy here.",
"goal": "persuasive",
"audience": "Startup founders",
"brandVoice": "Direct and useful"
}goal must be one of:
shorterpersuasiveformalseoaudience
Returns saved settings for the current session.
Updates session-scoped defaults and brand voice.
Request:
{
"defaultTone": "Bold",
"defaultContentType": "linkedin",
"brandName": "Bloom",
"brandIndustry": "Content marketing",
"brandVoice": "Warm, concise, and practical"
}{
"success": false,
"error": {
"code": "validation_error",
"message": "topic is required."
}
}Common statuses:
400- invalid JSON or validation error404- content item not found for the current session429- generation rate limit reached500- provider or storage failure
Content records include:
idsessionIdtopicaudiencetonecontentTypetitleexcerptcontentproviderimagescreatedAtupdatedAt
Image versions include:
idcontentIdstylepromptimageUrlproviderversioncreatedAt
- AI provider keys are used only on the server.
- Frontend components call internal REST endpoints only.
- Session ids are stored in signed HTTP-only cookies.
- Content reads, image updates, and deletes are scoped by
sessionId. - Generated text is rendered as React text, not raw HTML.
.env.local,.bloom-data, build output, and generated artifacts are ignored by git.
For deployment, set a strong SESSION_SECRET. Do not rely on local fallback secrets in a shared environment.
The local assessment build can use lightweight file-backed storage in .bloom-data/content-store.json. This keeps local demos simple and easy to inspect.
For Vercel/serverless or multi-instance deployments, set DATABASE_URL with a hosted Postgres provider such as Neon or Supabase. Local filesystem writes can be ephemeral or inconsistent across instances.
Recommended deployment:
- Vercel or Render for the Next.js app
- Vercel Marketplace Neon/Supabase Postgres for durable session history
- Real provider keys stored as platform secrets
BLOOM_SECURE_COOKIES=trueon HTTPS
Run:
npm run lint
npm run buildManual end-to-end checks:
- Generate a blog post, LinkedIn post, ad, and newsletter.
- Generate a visual companion for saved content.
- Regenerate with a different style.
- Confirm Dashboard and Library show saved text and image data.
- Open a saved bloom from Recent blooms.
- Copy, download, print/export, and delete saved content.
- Improve content for each goal.
- Confirm no client code calls external AI providers directly.
- No user accounts are implemented because the assessment requires saved content per session, not full authentication.
- Regeneration is prompt/context-based, not image-to-image, so it works across providers that do not support image input.
- File-backed persistence is intentionally simple for demo/submission. A hosted database is the next production step.
- Mock fallback is included only for local/demo resilience when provider keys are absent.