Guide

How to build an AI story generator with DeepSpace

Published July 22, 2026 · Updated July 22, 2026

An AI story generator turns a one-line idea — "a curious fox who learns to share, for a 4-year-old" — into a finished product: written pages, matching illustrations, and read-aloud narration. Building one means orchestrating three different AI APIs, a long-running pipeline, file storage, and payments. This guide shows how to do it on DeepSpace, where all four come from one npm package.

The walkthrough follows Story Nest, an open-source storybook generator built on the DeepSpace SDK and deployed at storynest.app.space. Everything described below is in its public source.

What you will build

The finished app takes a prompt through a create wizard and delivers an illustrated, narrated storybook a few minutes later — with generation running server-side, progress streaming live to the browser, and a full-screen reader for the result.

  • A create wizard for characters, lesson, age band, page count, and art style.
  • A three-model pipeline: an Anthropic model writes the outline and page text, Gemini illustrates every page in a consistent style, and ElevenLabs narrates each page.
  • A live progress screen and reader fed by useQuery — page rows flip to "ready" in real time as the pipeline completes them.
  • Credits, a Pro subscription, per-page re-rolls, and public shareable story links.

Start from the DeepSpace scaffold

One command scaffolds the app — worker, schemas, pages, auth, and deploy config: npm create deepspace@latest my-stories. The frontend and the Cloudflare Worker backend ship as a single package, and npx deepspace dev runs it locally with hot reload.

Before wiring any external API, list the catalog: npx deepspace integrations list prints every provider the platform proxies, with per-call pricing. Story Nest picked three — Anthropic for text, Gemini for images, ElevenLabs for speech — without creating an account at any of them.

Model storybooks, pages, and credits as collections

Story Nest stores a book as one storybooks record plus one pages record per page. The book row tracks pipeline state — status moves through outlining → illustrating → narrating → ready — and each page row carries its text, image prompt, and the R2 keys of its generated image and audio.

src/schemas/storybook-schema.ts
// src/schemas/storybook-schema.ts (abridged from Story Nest)
import type { CollectionSchema } from 'deepspace/worker'

export const storybookSchema: CollectionSchema = {
  name: 'storybooks',
  // 'published' reads: the owner always passes; everyone else only
  // sees rows where visibility === 'public'. Enforced server-side.
  visibilityField: { field: 'visibility', value: 'public' },
  permissions: {
    viewer: { read: 'published', create: false, update: 'own', delete: 'own' },
    member: { read: 'published', create: true,  update: 'own', delete: 'own' },
    admin:  { read: true,        create: true,  update: true,  delete: true },
  },
}

The visibilityField + read: 'published' pair is the SDK's pattern for owner-or-public reads: owners always see their own books, and flipping visibility to 'public' is all it takes to power the shared library and public story links. A separate credits collection tracks each user's balance; new accounts start with a signup grant.

Call LLM, image, and voice APIs through the integration proxy

DeepSpace apps call external AI APIs through the platform's integration proxy: one authenticated call shape, no API keys in the app, and usage metered to the app owner's account. A large language model (LLM), an image model, and a text-to-speech voice all look the same from app code.

Three providers, one call shape
// src/server/ai/outline.ts + audio.ts (abridged from Story Nest)
import type { ActionTools } from 'deepspace/worker'

// The SDK hands `tools: ActionTools` to server actions and jobs.
// One call shape for every provider — no API keys anywhere in the app.
// Every call returns an ActionResult envelope — narrow before use.
const outline = await tools.integration('anthropic/chat-completion', {
  model, max_tokens, system, messages,
})
if (!outline.success) throw new Error(outline.error)
const draft = outline.data // the provider's response body

const speech = await tools.integration('elevenlabs/generate-speech', {
  text, voice_id, model_id, output_format,
})
const audio = speech.success ? speech.data : null

Story Nest uses anthropic/chat-completion to write the outline — title, a character sheet, and per-page text — then Gemini image generation for the cover and every page (the character sheet rides along in each image prompt so characters stay visually consistent), and elevenlabs/generate-speech for read-aloud narration. The catalog spans text, image, video, and voice models plus services like web search, scraping, email, and GitHub.

Billing is a per-integration choice: by default the app owner pays for calls (which is what lets Story Nest charge its own users in credits instead), and a config file can switch any integration to user-pays. Integrations backed by per-user OAuth accounts — like Google — run as the signed-in user.

Run generation as a durable background job

A storybook takes dozens of model calls, so Story Nest runs the whole pipeline inside a JobRoom — DeepSpace's durable background-job queue built on a Cloudflare Durable Object. The job survives page refreshes and tab closes, and crash recovery comes free from the room's alarm loop.

  • Outline first, retried on transient failures; then one page row per outline page.
  • Bounded fan-out: cover, page images, and page audio generate in parallel with a concurrency cap.
  • Failures are per-page, not per-book: a failed step persists its reason on the page row, and the user re-rolls just that page.
  • Credits are reserved up front when the job is enqueued and refunded per failed step during aggregation.

The browser never orchestrates anything — it just watches. The progress screen is a useQuery over the book and its pages, and every status change the job writes streams to the client over the same real-time channel as any other record change.

Charge for it with credits and subscriptions

Story Nest monetizes with a free tier plus a Pro subscription, defined as data in the repo and synced to Stripe automatically on deploy. There is no hand-rolled Stripe integration anywhere in the app.

src/subscriptions.ts
// src/subscriptions.ts (abridged from Story Nest)
// Plain data, no SDK import needed — the DeepSpace CLI reads this
// file and syncs it to Stripe automatically on `npx deepspace deploy`.
export const subscriptionPlans = [
  { slug: 'free', name: 'Free', priceCents: 0 },
  { slug: 'pro',  name: 'Pro',  priceCents: 1200,
    yearlyCents: 10800, trialDays: 7 },
] as const

Generations are priced in credits — Story Nest reserves two credits per page when a book is enqueued — and Pro grants a monthly credit allowance. One-time credit packs are defined the same way in src/products.ts. Checkout, entitlement checks, and refunds all go through SDK primitives, so the app's payment surface is a few dozen lines of configuration and UI.

Hand the build to your coding agent

DeepSpace is designed so a coding agent can build this app end to end: the scaffold installs an agent skill, the integration catalog is one CLI call away, and payments are declarative. Copy the prompt below into Claude Code, Cursor, or any coding agent to build your own version.

Prompt for your coding agent
Build an AI story generator on DeepSpace (docs at https://docs.deep.space).

Setup: run `npx deepspace whoami` to check login, then `npm create deepspace@latest my-stories`. The scaffold installs the DeepSpace agent skill — read it before writing code. Run `npx deepspace integrations list` to see the API catalog before wiring any external service.

Data model: a `storybooks` collection (title, prompt, ageBand, pageCount, artStyle, status, progress, visibility) and a `pages` collection (text, imagePrompt, imageKey, audioKey, status). Use a visibility field with read: 'published' so owners see their own books and anyone can read books marked public.

Generation pipeline: run it as a durable background job in a JobRoom so it survives refresh and crashes. Steps: outline the story with an Anthropic model via tools.integration('anthropic/chat-completion'), create one page row per outline page, then fan out per-page illustration (Gemini image generation) and narration (`elevenlabs/generate-speech`) with bounded concurrency and retries. Store images and audio in R2 and update each page row's status as it completes.

Monetization: define subscription plans in src/subscriptions.ts (synced to Stripe on deploy — never hand-roll Stripe) and meter generations with a credits collection: reserve credits when a book is enqueued, refund per failed step.

UI: a create wizard (characters, lesson, age band, page count, art style), a live progress screen fed by useQuery on the pages collection, and a full-screen reader with narration playback.

Verify with `npx deepspace test`, then `npx deepspace deploy`.

The agent scaffolds the project, declares the schemas, wires the pipeline through the integration proxy, adds credits and plans, tests, and deploys to a live .app.space URL.

Go deeper

The DeepSpace documentation covers the APIs behind this guide: integrations, background jobs, file storage, payments, and deployment. The complete Story Nest source is on GitHub, and the comparison pages cover how this stack relates to Supabase, Firebase, and Convex.

Frequently asked questions

Do I need my own API keys for Anthropic, Gemini, or ElevenLabs?

No. DeepSpace apps call external AI APIs through the platform’s integration proxy — one call shape, no provider accounts, no keys in the app. Usage is metered to the app owner, and any integration can be switched to user-pays billing in a config file.

What happens if generation fails halfway through a book?

The pipeline runs as a durable background job, so failures are contained per step. Transient errors are retried; a page that still fails records its reason and can be re-rolled individually; and credits reserved for failed steps are refunded when the job aggregates.

Can users share their generated stories publicly?

Yes. Each storybook has a visibility field, and the schema grants read access with the SDK’s owner-or-public rule: owners always see their own books, and books flipped to public are readable by others through a shareable link.

Is Story Nest open source?

Yes. The full source is public at github.com/deepdotspace/storynest, and the deployed app runs at storynest.app.space. The guide above describes the code as it exists in that repository.