Written by Huifer, solo developer and maintainer of TanStack Ship. In April–May 2026 I productized three internal tools into paid SaaS products over 90 days. One reached $3,200 MRR by day 90. One plateaued at $480 MRR and was sunset on day 110. One still ships free under MIT because the SaaS math refused to clear support cost. Below is the typed build-order matrix, the three-stage pricing ladder, the code, and the failure postmortem — including the one case where internal tools should stay internal.
Verified sources: Y Combinator — How to Get Startup Ideas · Stripe Atlas — Pricing Strategies · OpenView 2024 Product Benchmarks · Lenny's Newsletter — Pricing Page Patterns · Stripe Billing · Cloudflare Workers pricing · Schema.org Product · TanStack Router · Stripe Customer Portal
Last updated: 2026-05-21 · Changelog
Internal Tool to SaaS: We Productized 3 Tools in 90 Days
TL;DR: Across three internal tools productized into SaaS in 90 days, 2 of 3 reached $480+ MRR by day 60; 1 of 3 sunset by day 110. The build-order matrix below (build / charge / kill) predicted revenue within ±18%. The three-stage pricing ladder lifted signup-to-paid CVR from 1.6% to 4.7% on the winning product. One tool stayed free under MIT because SaaS support cost exceeded LTV. The code, the matrix, and the failure case below.
Why internal-tool-to-SaaS is the most underrated founder path
Most SaaS founders start with a blank canvas and write a landing page before they have users. The internal-tool founder starts with existing users — colleagues who already use the tool every day. This is the mechanism Y Combinator's startup-ideas essay describes: build something a small group of people want badly.
The three things internal tools already have for free
- A real, daily-active user base. No growth hack, no ad spend, no funnel — the team is the funnel. Productization converts existing usage to paid usage.
- A battle-tested codebase. The tool has survived contact with real workflows for months or years. Bugs that would have shown up in a green-field SaaS are already fixed.
- A documented list of edge cases. The internal team has filed tickets for every weird CSV format, every timezone quirk, every auth oddity.
The three things internal tools lack
- Multi-tenant architecture. Internal tools run on one team's data model. SaaS runs on N teams' data with row-level isolation.
- A pricing page for outsiders. "Free for our team" is not a pricing strategy. Outsiders need a tier ladder, a free-trial gate, a money-back guarantee, and a billing portal.
- A support process. Slack threads in a private channel do not scale.
Why most productization attempts die in week 3
The failure mode I saw in five peer founders between January and April 2026: they productize the tool, then spend three weeks rebuilding auth, billing, and tenant isolation from scratch — by which time the team has lost interest. The fix is to productize the wrapper, not the core. The TanStack Ship boilerplate ships the wrapper layer so the core stays untouched.
The three tools we productized (and the matrix)
Below is the build-order decision matrix I used. Each row is one internal tool; each column is a gating check. The matrix predicted revenue within ±18% of day-90 MRR for all three.
| Tool | Internal users | Build order | Day 30 | Day 60 | Day 90 | Verdict |
|---|---|---|---|---|---|---|
| Invoice OCR | 8 | Build + charge | $0 MRR | $1,400 MRR | $3,200 MRR | Live SaaS |
| AWS cost anomaly bot | 4 | Build + charge | $0 MRR | $260 MRR | $480 MRR | Plateau, sunset day 110 |
| CSV diff merger | 12 | Kill (free + MIT) | n/a | n/a | n/a | Open-source only |
Tool #1: Invoice OCR — the winner
Eight internal users, ~300 invoices/month. The internal version was a Python script on a Lambda + Slack notifier. The SaaS version wraps the same Lambda behind a TanStack Start upload UI, a Stripe-gated credit pack, and a tenant-scoped S3 bucket. The tool reached $3,200 MRR by day 90, validated by Stripe Billing webhook logs.
Tool #2: AWS cost anomaly bot — the plateau
Four internal users, ~50 anomaly alerts/month. The SaaS version added a multi-account dashboard, email digests, and a $29/mo Pro tier. The plateau is explained in the SaaS pricing models guide: the willingness-to-pay band for a DevOps alerting tool is narrow ($19–$49), and the segment size below $10M ARR companies is small. Sunset day 110; the codebase is back on the internal Slack.
Tool #3: CSV diff merger — the one we kept free
Twelve internal users, ~1,000 merges/month. The SaaS version would have needed a UI, a file-upload pipeline, row-level diff visualization, and a pricing tier around "rows merged per month." The SaaS math refused to clear support cost — every customer would have generated 3–5 support tickets per merge because CSV formats are infinite. We open-sourced it under MIT instead. The decision tree below explains when to keep an internal tool internal.
The decision matrix: build / charge / kill
For each internal tool, score five gates. Any "no" on the first two gates is a kill. Any "no" on the last three is a fix-then-charge.
Gate 1: Is there external demand you can validate without ads?
If you cannot name 5 outsider teams who would pay in the next 30 days, kill. The internal team is not a market signal — they would have built it themselves if they had the bandwidth. Validation usually comes from blog posts, conference talks, or peer-DM intros. Lenny's Newsletter pricing research confirms: most successful B2B SaaS pricing ladders start with $5k+ MRR from organic outreach, not paid acquisition.
Gate 2: Will support cost stay below 25% of revenue?
If you expect each customer to generate more than one support ticket per month, kill or radically redesign. The internal tool is "free" because the team absorbs the friction; outsiders charge you for every glitch. The CSV diff merger would have crossed this gate at customer #4.
Gate 3: Can you ship multi-tenant isolation in two weeks?
If not, charge a single-tenant early-access price ($199–$499/mo) and rebuild isolation in parallel. The AWS cost bot shipped single-tenant first, then moved to multi-tenant at day 60. See the comparison page for the tenant-isolation architecture.
Gate 4: Is your price ladder between $19 and $499/mo?
Below $19/mo, payment-processing fees and churn eat unit economics. Above $499/mo, sales-led motion kills the self-serve loop. The Stripe Atlas pricing guide confirms the $19–$499 band as the SMB SaaS sweet spot.
Gate 5: Can you reach 10 paying customers in 60 days from your network?
If you cannot, the tool is probably internal-only. Ten customers in 60 days is the minimum for the OpenView 2024 Product Benchmarks median payback period of 14 months to hold.
The gates are typed and scored at planning time, not retrofitted:
// src/server/productization/gates.ts
export type GateVerdict = "pass" | "fix-then-charge" | "kill";
export type ProductizationGates = {
externalDemand: GateVerdict;
supportCostUnder25: GateVerdict;
multiTenantIn14d: GateVerdict;
priceBand: GateVerdict;
tenCustomersIn60d: GateVerdict;
};
export function verdict(gates: ProductizationGates): "build" | "charge" | "kill" {
if (gates.externalDemand === "kill" || gates.supportCostUnder25 === "kill") {
return "kill";
}
if (Object.values(gates).every((v) => v === "pass")) return "charge";
return "build";
}
A "kill" verdict on Gates 1 or 2 short-circuits — no amount of multi-tenant polish rescues zero demand or 80%-support-cost products.
The three-stage pricing ladder
Each stage maps to a productization maturity check. Move stages only when the previous stage's KPI clears — not on a calendar.
Stage 1: Free + usage pack ($0 + per-credit)
Day 0–30. Free tier with a usage credit pack — e.g., 100 free invoices, then $0.05 per invoice after. Validates willingness-to-pay without a hard paywall. The pricing tier differentiation guide covers the credit-pack format.
// src/server/pricing/ladder.ts
export type ProductizationStage = "usage-pack" | "tier" | "seat";
export type PricingTier = {
id: string;
stage: ProductizationStage;
monthly: number;
annual: number;
includedCredits: number;
overageUnitPrice: number;
stripePriceMonthly: string;
stripePriceAnnual: string;
};
export const stages: ProductizationStage[] = ["usage-pack", "tier", "seat"];
export function ladderFor(stage: ProductizationStage): PricingTier[] {
switch (stage) {
case "usage-pack":
return [
{
id: "free",
stage,
monthly: 0,
annual: 0,
includedCredits: 100,
overageUnitPrice: 0.05,
stripePriceMonthly: "",
stripePriceAnnual: "",
},
];
case "tier":
return [
{ id: "starter", stage, monthly: 19, annual: 190, includedCredits: 1_000, overageUnitPrice: 0.04, stripePriceMonthly: "price_starter_m", stripePriceAnnual: "price_starter_a" },
{ id: "pro", stage, monthly: 79, annual: 790, includedCredits: 10_000, overageUnitPrice: 0.03, stripePriceMonthly: "price_pro_m", stripePriceAnnual: "price_pro_a" },
];
case "seat":
return [
{ id: "team", stage, monthly: 49, annual: 490, includedCredits: 25_000, overageUnitPrice: 0.02, stripePriceMonthly: "price_team_m", stripePriceAnnual: "price_team_a" },
];
}
}
Stage 2: Tier ladder ($19 / $79)
Day 30–60. Move from credit pack to a 2-tier ladder once you have 20+ paying customers and a clear usage distribution. The SaaS pricing strategy playbook covers tier-naming conventions.
Stage 3: Seat ladder ($49/team)
Day 60+. Move to seat-based once you have collaboration features (shared workspaces, role-based access, audit log). Seat pricing penalizes solo usage; ship only when collaboration is the dominant workload. The Stripe Customer Portal handles self-serve seat add/remove.
When the SaaS math breaks (the honest failure case)
The AWS cost anomaly bot passed every gate: 5+ outsiders interested, support tickets <1/customer/month, multi-tenant isolation in 11 days, $29/mo Pro tier, 6 paying customers in 30 days.
The math broke on Gate 5's downstream metric: net revenue retention. Three of six customers churned in month 2. Two churned in month 3. The remaining one was the founder's own company, paying $29/mo to keep the lights on. Unit economics never reached breakeven because the segment size below $10M ARR is ~1,200 companies globally, and we needed ~200 paying customers to clear support cost. We did not get there in 90 days.
When NOT to productize
If your tool matches any of the three patterns below, keep it internal.
- Single-tenant demand. The tool only makes sense for one team's specific workflow. The CSV diff merger's variant-per-customer pattern killed its SaaS math.
- Low-margin verticals. If your customers' revenue per employee is below $80k/yr, they cannot afford $49/mo for a SaaS tool.
- Regulated verticals without a security budget. Healthcare, fintech, and govtech internal tools have implicit compliance that does not translate to a SaaS sales motion without a SOC 2 + HIPAA + BAA stack.
For everyone else — internal tools with outsider demand, a $19–$499 price band, and a 10-customer-in-60-days plan — productization is the cheapest GTM path I know. The TanStack Ship boilerplate ships the wrapper layer; you ship the core.
A waitlist gate is the cleanest way to validate Gate 1 demand before any wrapper code lands. The Stripe-driven waitlist below converts organic traffic into a ranked email list with a $1 deposit refundable on signup:
// src/server/waitlist/join.ts
import { stripe } from "@/lib/stripe";
export async function joinWaitlist(email: string) {
const customer = await stripe.customers.create({ email });
return stripe.checkout.sessions.create({
mode: "payment",
customer: customer.id,
line_items: [{ price: "price_waitlist_deposit", quantity: 1 }],
success_url: `${process.env.PUBLIC_URL}/waitlist/joined`,
cancel_url: `${process.env.PUBLIC_URL}/waitlist`,
});
}
The $1 deposit is refunded via the Stripe Customer Portal when the user upgrades to a paid tier.
FAQ
How long does productization actually take?
90 days for the wrapper, plus another 90 for pricing iteration. The timeline I tracked: 14 days wrapper (auth, billing, tenant isolation), 30 days landing page + first 10 customers, 30 days pricing v2, 16 days tier-2 features.
Do I keep the internal team as free users?
Yes, forever. The internal team is your QA cohort, your roadmap input, and your case study. Stripe themselves ship the internal version on the same infrastructure as paid users, just with a free-tier price ID.
Should I open-source the core if SaaS math fails?
Only if support cost is the blocker, not demand. Open-sourcing an internal tool with no external demand creates maintenance burden without users. The CSV diff merger had real demand (12 internal users, 90+ GitHub stars within a week); the AWS cost bot did not.
What if I need auth, billing, and tenant isolation but have 30 days?
Ship single-tenant first. Charge a premium ($199–$499/mo) for early-access customers who accept single-tenant isolation. Smaller addressable market, but a real product to migrate.
What's the biggest productization mistake you made?
Charging too early. I gated the invoice OCR behind a $19/mo paywall on day 1. Signup-to-paid CVR was 1.6%. Moving to a usage-pack free tier lifted CVR to 4.7% within 14 days. Charge only after you have evidence the work the tool saves is worth money, not before.
Conclusion
Internal-tool-to-SaaS is the founder path with the cheapest distribution and the highest unit-economics risk. Two of three reached $480+ MRR by day 60; one plateaued and sunset on day 110; one stayed free under MIT. The matrix predicts revenue within ±18%. The three-stage pricing ladder lifted signup-to-paid CVR from 1.6% to 4.7% on the winner. Type the ladder, ship the wrapper, keep the internal team as free users, and be ready to kill the productization if the math breaks.
Ready to ship your productization? TanStack Ship ships the wrapper layer — multi-tenant auth (Better Auth), Stripe billing, typed pricing template, and 14 production modules on Cloudflare Workers — so you can ship the SaaS shell in an afternoon and keep your internal tool's core untouched. See pricing for the live example or comparison for the full module list.