# How to build a SaaS app with AI from a day-1 owned monorepo

> Build a SaaS app with AI from a day-1 owned monorepo — auth, billing, and schema as product outcomes agents extend, not an IDE prompt-loop or demo tour.
> By Dave · 2026-09-14
> Source: https://otf-kit.dev/blog/how-to-build-a-saas-app-with-ai

Coding agents are excellent at extending a repository they can read. The hard part of building a SaaS app with AI is not the next prompt — it is what exists on day 1. If day 1 is a blank chat or a rented preview, every later session rebuilds auth, billing, and the data model as chores. If day 1 is an owned monorepo where those three are already product outcomes, agents extend customer-facing surfaces instead of reinventing the spine.

This post is a how-to for that build order: owned monorepo first, then auth as a product outcome, then billing as a product outcome, then schema as a product outcome, then agents. Catalog lane: saas → saas-dashboard. It is not the [stay-in-the-IDE SaaS prompt loop](https://otf-kit.dev/blog/ship-saas-without-leaving-ide), not the [live demo tour](https://otf-kit.dev/blog/saas-kit-live-tour), and not the [Retool / Appsmith vs product-repo contrast](https://otf-kit.dev/blog/otf-vs-retool-appsmith-dashboard). Those posts exist. It is also not the [admin panel](https://otf-kit.dev/blog/how-to-build-an-admin-panel-with-ai) or [analytics dashboard](https://otf-kit.dev/blog/how-to-build-an-analytics-dashboard-with-ai) how-tos — those order domain screens. This one orders the SaaS product spine.

## Day 1 means an owned monorepo, not a prompt transcript

A SaaS product is a durable repository: client, API, database migrations, env contracts, deploy script, and agent config living together under one git root you control. Day 1 ownership means you can clone, run locally, review diffs, and ship under your domain without asking a generator host to export later.

OTF's SaaS Dashboard kit is listed on [https://otf-kit.dev/templates/saas-dashboard](https://otf-kit.dev/templates/saas-dashboard) and in the kits table on [https://otf-kit.dev/docs/templates/overview](https://otf-kit.dev/docs/templates/overview). Standalone price is $99; also in the Everything Bundle on [https://otf-kit.dev/pricing](https://otf-kit.dev/pricing). Live proof before checkout: [https://saas.otf-kit.dev](https://saas.otf-kit.dev). Purchase path: Stripe checkout, license email with a private GitHub invite, `git clone`, install, `bun dev`, then the kit deploy script when you are ready.

Public product claims that matter for day-1 ownership:

- Full-stack scaffold in one repo (frontend + backend)
- Auth wired (email + Google OAuth + a demo path)
- Postgres with schema, migrations, and seed data
- Stripe Checkout + webhook + license-delivery email
- Agent handoff files: `CLAUDE.md`, `.cursorrules`, `AGENTS.md`, plus 20+ tested prompts under `ai/prompts/`
- Docs for getting started, architecture, customisation, and deployment

The MIT SDK kits consume is public at [https://github.com/otf-kit/sdk](https://github.com/otf-kit/sdk). Kits are commercial source after purchase; the SDK is the shared component layer.

Treat day 1 as "the product repository exists and runs." Do not treat day 1 as "I described a SaaS in chat and got a preview."

## Auth, billing, and schema as product outcomes — not plumbing tickets

Plumbing framing asks: how many weeks until login works, until checkout works, until tables exist. Product-outcome framing asks different questions:

- **Auth outcome:** Can a customer create an account, sign in, join an org, and land in a branded session you control?
- **Billing outcome:** Can a customer pick a plan, pay, and get entitlements that gate product features from verified payment state?
- **Schema outcome:** Does the database already encode the product's nouns (workspace, team, member, issue, notification) so features map to rows instead of ad-hoc JSON?

Those outcomes are what users feel. Agents should extend them — invite flows, plan limits, new entities — not invent them from a greenfield prompt every sprint.

This is adjacent to, but not the same as, the weeks-saved thesis in [auth and billing you don't hand-roll](https://otf-kit.dev/blog/auth-billing-you-dont-hand-roll). That post argues undifferentiated setup is expensive. This post argues the day-1 monorepo should already expose auth, billing, and schema as shippable product surfaces your AI coding tools keep editing.

![Auth, billing, and schema as product outcomes — not plumbing chores](https://cdn.otf-kit.dev/blog/how-to-build-a-saas-app-with-ai/inbody-01-outcomes-20260914a.jpg)

## Auth as a product outcome

Auth is not "add a provider." Auth is the first product screen customers trust: sign-up, sign-in, session continuity, org membership, and logout that matches your brand and domain.

On day 1 of an owned saas-dashboard monorepo, the outcome you verify is concrete:

1. Sign-up creates a user row and a session.
2. Sign-in restores that session without a second product.
3. Org / team membership resolves which workspace the dashboard loads.
4. Protected API routes reject unauthenticated calls the same way the UI does.

Write the product contract before you ask an agent to "improve auth":

```ts
type AuthProductOutcome = {
  userId: string
  orgId: string
  role: "owner" | "admin" | "member"
  sessionValid: boolean
}

function canEnterDashboard(session: AuthProductOutcome): boolean {
  return session.sessionValid && Boolean(session.orgId)
}
```

When Cursor or Claude Code extends auth, point them at that contract plus the kit's existing auth routes and agent docs. Ask for a named product change ("add invite-by-email that creates a `teamMember` row") — not "wire login from scratch."

## Billing as a product outcome

Billing is not a checkout button. Billing is the paid relationship: plan selected, payment verified, entitlement granted, access revoked when the subscription is no longer good.

Stripe's own subscription overview is explicit that subscriptions move through statuses (`trialing`, `active`, `past_due`, `canceled`, and related states) and that you should provision access when the subscription is in good standing, using webhooks and entitlements rather than trusting the browser ([Stripe subscriptions overview](https://docs.stripe.com/billing/subscriptions/overview)). For an AI-built SaaS, that means your monorepo owns the product gate; the provider owns card storage and retry collection.

Day-1 billing outcome checklist:

1. Checkout starts on the server with your price IDs.
2. A signed webhook updates local subscription state.
3. Feature gates read local state (or provider entitlements), never a client-only flag.
4. Cancel / past-due paths degrade access in the product UI you own.

Keep a small entitlement map in the repo so agents extend plans without inventing payment logic:

```ts
type PlanId = "starter" | "growth"

type Entitlement = {
  planId: PlanId
  maxSeats: number
  canUseAnalytics: boolean
}

const ENTITLEMENTS: Record<PlanId, Entitlement> = {
  starter: { planId: "starter", maxSeats: 3, canUseAnalytics: false },
  growth: { planId: "growth", maxSeats: 25, canUseAnalytics: true },
}

function allowFeature(
  planId: PlanId,
  feature: keyof Omit<Entitlement, "planId" | "maxSeats">,
): boolean {
  return Boolean(ENTITLEMENTS[planId][feature])
}
```

Ask agents to add a plan field or a gate — not to redesign card handling. Deeper payment lifecycle detail lives in [payments you don't hand-roll](https://otf-kit.dev/blog/payments-you-dont-hand-roll). Here the point is paid access as a day-1 product outcome.

## Schema as a product outcome

Schema is the product's vocabulary. If day 1 has no migrations and seed data, every AI session invents table shapes. If day 1 already has workspace, team, member, issue, comment, notification, and related entities (as the saas-dashboard kit documents), agents add columns and relations against a known contract.

Product-outcome test for schema:

- Can you explain the app in the nouns the database already uses?
- Does seed data let you click a realistic workspace on first run?
- Do query hooks / API routes sit beside the tables they own?

When you prompt an agent, name the entity and the migration boundary:

```bash
# Example agent ask (shape, not a required filename)
# "Add a `savedView` filter field used by the issues list.
#  Migration + API + UI. Do not invent a second issues table."
```

Schema-as-outcome keeps the monorepo coherent. Schema-as-afterthought is how AI-coded SaaS projects feel disposable — the same failure mode described in [why AI-coded projects feel disposable](https://otf-kit.dev/blog/ai-coded-projects-feel-disposable).

![Agents extend the owned SaaS monorepo spine instead of regenerating it](https://cdn.otf-kit.dev/blog/how-to-build-a-saas-app-with-ai/inbody-02-monorepo-20260914a.jpg)

## How AI agents should extend the spine (without turning this into an IDE-loop post)

Once auth, billing, and schema exist as outcomes, the agent job is incremental product work:

1. Read `CLAUDE.md` / `.cursorrules` / `ai/prompts/` so the kit conventions are load-bearing.
2. Change one outcome surface at a time (invite flow, plan gate, new entity).
3. Review the diff in git; run the app; keep the migration with the feature.

That is complementary to the in-IDE loop essay — this post stops at the precondition. Without day-1 outcomes, the loop regenerates plumbing; with them, it ships product.

UI pieces can come from the public SDK (`@otfdashkit/ui`, `@otfdashkit/tokens`) at [https://github.com/otf-kit/sdk](https://github.com/otf-kit/sdk). The kit remains the product repo.

## A concrete day-1 sequence after you own the repo

1. Accept the GitHub invite, clone, install, run the local dev command from the kit docs.
2. Walk auth as a customer: sign up, sign in, confirm org context loads the dashboard.
3. Walk billing in test mode: start checkout, confirm webhook updates local state, confirm a gated screen respects entitlement.
4. Open the schema: skim migrations and seed; map three product nouns to tables.
5. Write one agent prompt that extends a single outcome (example: seat limit copy on the teams page tied to `maxSeats`).
6. Commit the diff; deploy with the kit script when you want a public URL.

If any step fails, fix that outcome before asking for feature theater. A SaaS app with AI fails when prompts outrun the spine.

## What "you actually own" means for a SaaS build

Ownership is not a slogan on a marketing page. It means:

- Source under your GitHub account after purchase
- Auth sessions and org data in your database
- Billing state you can query without opening a canvas host
- Schema migrations in git history next to the features they enable
- Agent configs that travel with the repo when teammates clone it

Compare that to a chat preview or internal-tools host: screens may arrive faster, but auth, billing, and schema stay someone else's outcomes.

Build the SaaS app with AI from an owned monorepo on day 1 — auth, billing, and schema as outcomes customers experience — then let agents extend that spine.

Browse the kit and try the live demo from [https://otf-kit.dev/templates/saas-dashboard](https://otf-kit.dev/templates/saas-dashboard) when you want the commercial path; the SDK remains free at [https://github.com/otf-kit/sdk](https://github.com/otf-kit/sdk).

## Sources

- [OTF SaaS Dashboard kit](https://otf-kit.dev/templates/saas-dashboard)
- [OTF templates overview](https://otf-kit.dev/docs/templates/overview)
- [OTF site](https://otf-kit.dev)
- [otf-kit/sdk on GitHub](https://github.com/otf-kit/sdk)
- [Stripe: How subscriptions work](https://docs.stripe.com/billing/subscriptions/overview)
- [OTF live SaaS demo](https://saas.otf-kit.dev)
