Case study/Built solo, live in production

CarDealerTool.
A dealership,
run from one screen.

An all-in-one dealer management system for independent used-car dealers in the US — inventory, deal desking, in-house financing, compliance and a public storefront for every dealer on the platform. I designed it, built it and run it myself.

See the product live cardealertool.com · pricing and trial live there
№ 01 / The hard part

Why this isn't
a CRUD app with a car table.

Most dealer software is a list of vehicles with a contact form bolted on. The moment a dealer finances its own customers, the problem changes category.
Regulated domain

In-house financing is a lending business

Buy-here-pay-here dealers are the lender. That means loan origination and servicing, payment schedules and collections, delinquency handling, repossession and charge-off — and reporting the whole thing to the credit bureaus in their format, on their cadence. Get the arithmetic or the reporting wrong and the dealer has a legal problem, not a bug report.

Paper that has to be right

Contracts generate themselves

Every deal produces a stack of documents whose numbers must agree with the deal desk down to the cent, then get signed electronically and stored where an auditor can find them. Auto-generated paperwork and e-signature aren't features on a list — they're the point where a deal either closes on the lot or doesn't.

Multi-tenant

Every dealer gets their own public site

Not a shared directory: each tenant gets a real storefront on its own subdomain, with its own inventory, branding, pages and SEO — generated by the platform and updated the moment a car changes status in the back office. One codebase, isolated data, N public websites.

Nobody starts from zero

Migration is the first feature

A dealer switching systems arrives with years of inventory, deals and customer history in DealerCenter, live lender connections, and listings syndicating to AutoTrader and Cars.com. Import, lender submissions and marketplace feeds all had to work before day one, or the product is unusable by exactly the people it's for.

№ 02 /What's inside

Eighteen modules,
one system.

Grouped the way a dealership actually works — a car comes in, gets sold, gets financed, gets serviced by the office.
18modules · 6 groups · one codebase

Inventory03

  • Inventory Management
  • Window Stickers & Hang Tags
  • AutoTrader & Cars.com Feeds

Selling04

  • Deal Desking
  • Lender Submissions
  • Auto-Generated Paperwork
  • E-Signature & Digital Contracting

Financing04

  • Loan Origination & Servicing
  • Payments & Collections
  • Repossession & Charge-Off
  • Credit Reporting & Compliance

Customers02

  • Lead Pipeline & Follow-Ups
  • Bulk Email & SMS

Web03

  • Smart Storefront Search
  • Dealer Website Included
  • Page Builder & SEO

Operations02

  • Multi-Location & Roles
  • Reports & Analytics
№ 03 / Multi-tenant, live

A tenant you can
click right now.

“Every dealer gets their own website” is a weak sentence. It's a strong one when you can open a working store with real cars on it.

Shoreline Auto Center is a used-car dealership in Virginia Beach, VA. Their public website isn't a demo I built to show off the platform — it's the site their customers use, generated and served by CarDealerTool from the same back office the dealership works in every day.

Every car on it, every price, every financing page comes out of the tenant's own data. Change a vehicle's status in the office and the storefront reflects it. That's the whole multi-tenant argument, and it's one click away instead of a claim in a bio.

Live tenant storefront
shorelineautocenter.cardealertool.com
  • Full inventory, straight from the dealer's back office
  • Filtering by make, budget and body style
  • Financing pages, testimonials, FAQ, contact
  • Its own subdomain, branding and SEO surface

A real dealership's public site — not a sandbox, not staged data.

№ 04 /How it's built

The decisions
that mattered.

Next.js and React on the surface. The interesting parts are underneath.
Next.js 16React 19TypeScriptMySQLPrismaNextAuth v5StripeResendsharppdf-lib
Architecture
Next.js App Router, one codebase serving both the dealer application and every tenant storefront. Auth is NextAuth v5 with JWT sessions; billing runs through Stripe subscriptions with a webhook. There is no dedicated job queue — nine periodic tasks run as secret-protected HTTP routes called by cron: BHPH due dates, automated customer communications, credit bureau reporting, feed sync, accounting sync, expiring account suspension. Long imports persist per-row state in the database rather than holding it in memory, so a restart can't lose an import halfway through. Files are on local disk, not object storage — a deliberate stage decision, and a mandatory migration at the second node.
Data model
MySQL through Prisma, 80 models. The core is Tenant → Vehicle, Customer, Deal. Every internally financed deal carries a BhphAccount one-to-one, and that account holds the credit terms frozen at origination — principal, APR, number of instalments — kept separate from the deal, so editing the transaction later never rewrites credit history. Under the account sit two distinct tables: the schedule (what is owed and when) and the actual payments (what was collected, by what method). Amounts are Decimal, not float — in a system that computes interest, floating point isn't an option.
Multi-tenancy
One database, row-level isolation: 60 of the 80 models carry a tenantId, the JWT session carries tenant and role, and every query is explicitly scoped. Users belong to tenants many-to-many — a single-dealership user gets it straight in the session, a multi-dealership user picks one and the session reloads with the role from that membership. Subdomains resolve in a proxy that reads the Host and rewrites internally: shorelineautocenter.cardealertool.com/inventory becomes /dealers/shorelineautocenter/inventory internally, and the internal path redirects back to the clean one. The host parser is a pure function reused by sitemap, robots and the 404 page, so the four can't drift apart. A custom domain is a field on the tenant; every public URL — canonical, sitemap, robots — is built through one function, so it switches over the moment the field is set. The application side is complete; TLS certificate automation for arbitrary domains is next.
The hardest part
The Metro 2 file generator for credit bureau reporting. Fixed-width, written from scratch against the CDIA specification: 426-character records, header, one segment per account, trailer, padded with spaces and zeros under strict conventions. It wasn't hard as programming — it was hard as specification. Every field has a mandated position, length and format; a single deviation invalidates the whole file, and bureau feedback is slow and opaque. The real work was deciding what to populate realistically for a dealer who finances his own customers, and what to leave empty. What's shipped covers the ordinary case; a bureau-certified submission needs additional fields — a known stage, not an assumption that it's finished.
№ 05 / SEO architecture

The pages a dealer site
should have — generated.

Not a marketing layer bolted on afterwards. The URL surface is a product of the inventory.

Every tenant site generates its own category pages out of the real stock: make, model, body style, fuel type, price threshold, the combinations of those, plus areas served. Nobody creates them by hand.

They're gated by a threshold: below a certain number of cars, the URL 301s to its parent facet instead of existing as a thin page. A dealer with no Jeeps doesn't get a Jeep page. Filters stay crawlable parameter URLs but canonicalise to the clean category page, so the signal consolidates instead of fragmenting.

Generated, not authored
  • used-bmw-for-sale-virginia-beach-va
  • used-ford-f150-for-sale-virginia-beach-va
  • used-suv-for-sale-virginia-beach-va
  • used-hybrid-cars-for-sale-virginia-beach-va
  • used-cars-under-15k-virginia-beach-va
  • used-toyota-suv-for-sale-virginia-beach-va
Structured data

AutoDealer, ItemList, FAQPage and BreadcrumbList on categories; Car, Offer, Brand and engine specs on each vehicle page.

No phantom markup

The FAQ in the schema is the same text as the visible accordion — never a second, invisible version written for crawlers.

Derived where the data was missing

The import from the previous platform carried no fuel type, so it's derived from the model name. Without that, every fuel page would simply not exist. Sitemap is per tenant, generated from inventory.

Then I read the coverage report.

The URL architecture was right. The internal link graph cancelled it out.

110 pages were discovered from the sitemap and never crawled by Google. 45 make+model pages and 20 price-threshold pages had no internal link pointing at them at all, and the inventory page server-rendered 13 of 54 vehicles — the rest loaded from JavaScript, with no pagination a crawler could follow.

The proof is clean: the Chevrolet page, linked from two places, is indexed. The Chevrolet Silverado page, with zero links to it, was never touched. You don't see this by looking at rankings. You only see it by reading the coverage report. The fix — server-rendered pagination and a real category index — is specified and next.

This is the work I do for rental sites
110

Pages discovered from the sitemap and never crawled

65

Of those with zero internal links pointing at them — 45 make+model, 20 price thresholds

13

Of 54 vehicles server-rendered on the inventory page; the rest client-side, unpaginated

№ 06 / Where it is

Live, in daily
use.

Shipped software with someone's business depending on it — not a portfolio piece that stopped at launch.
In production

Shoreline Auto Center in Virginia Beach, VA runs its day on it — inventory, deals, financing and its public site. 54 vehicles, 101 transactions, 4 users.

Still shipping

Development is ongoing: modules get deepened as the dealer hits real edge cases, which is the only useful source of requirements in this domain.

Supported by me

I'm the person who takes the call when something's wrong at 8am on a Saturday, and the person who fixes it. Same person, both roles.

№ 07 / If you hire me

This is the kind of
system I deliver — and
then operate.

CarDealerTool is what happens when I own something end to end: the domain research, the architecture, the code, the migration path, and the phone when it breaks. If you're weighing whether to bring one developer into something serious — a platform, a rebuild, a product that has to hold real money and real compliance — this is the evidence, and it's live.

← All projects

Talk about your project See the product