Florida Massage Elite is a self-running Florida therapist directory. It was built on Next.js 15, Supabase, and Stripe in five weeks. Every therapist is identity-verified through Stripe Identity before publishing. Subscriptions run through auto-switching Stripe pricing tiers. Programmatic city and service pages generate themselves from the database. The owner only handles approvals.

Key takeaways

  • Built in five weeks on Next.js 15, Supabase, and Stripe, matching the Florida Massage Elite case study.
  • Stripe Identity checks every therapist's ID and selfie before a profile goes live. No license files are stored on the server.
  • A pending-approval queue keeps the previous profile version live while edits wait for review. Nothing disappears from search mid-approval.
  • Programmatic city and service pages, along with schema markup, generate on their own as new therapists get approved.
  • Founding-30 pricing locks in early-adopter rates. New signups auto-switch to higher tiers once the cap is reached.

Updated 15 August 2026: sources added, experience claims checked against our project record, summary added.

The Problem: Trust Is Expensive When You're Manual

Florida's massage industry has a real trust problem online. Unlicensed practitioners list themselves everywhere, and consumers can't tell who's legitimate. The vision was a Florida massage therapist directory where every therapist is ID-verified before their profile goes live. Not spot-checked. Not self-reported. Verified.

The catch: the owner is not a full-time operator. They can't act as tech support, chase incomplete signups, check documents by hand, or answer the same five pricing questions every day. The directory had to run like a machine: one that signs up therapists, verifies them, and takes payment. It also had to publish pages on its own and only alert the owner when a human decision was truly needed.

That constraint shaped every choice in the build. The guiding question was simple: does this feature need the owner to do something? If yes, could it run on its own instead? Usually it could.

The Stack and Why Each Piece Earned Its Spot

Next.js 15 and Tailwind on Vercel Pro. Supabase for Postgres and auth. Stripe for payments and identity checks. Google Maps for location. Resend for transactional email. An AI chatbot for front-line support.

Nothing exotic. That's the point. The client owns the GitHub repo, transferred at handoff rather than held back. The Supabase project sits under their own account, and can be exported as SQL or CSV anytime. It's a standard Next.js stack with zero vendor lock-in. Running cost lands around $20-45 per month once it's live.

A similar directory build, the 137,000-listing coffee, pub, and restaurant directory on Next.js, Supabase, and Vercel, follows the same idea. That project holds far more raw data, but this one is more complex per listing. Florida Massage Elite needed verification, subscriptions, approval queues, and guided onboarding. That onboarding has to feel easy for a therapist filling it out on a phone late at night after a full day of clients. That last part matters more than people expect.

Self-Serve Onboarding: A Six-Step Wizard That Does the Heavy Lifting

Therapists sign up through a guided wizard. Six steps, each one focused, each one saving progress so a half-finished signup doesn't just vanish.

  1. Basic info -- name, location, bio.
  2. Services -- picked from a 20+ massage-type taxonomy (deep tissue, Swedish, prenatal, sports, the full list). This taxonomy also drives the programmatic service pages, covered below.
  3. Pricing -- duration rows for 30, 60, 90, and 120 minutes, with separate columns for in-call and out-call rates. Sounds simple. Getting the mobile layout right for this grid took more work than almost anything else in the build.
  4. Availability -- a day-by-day hours picker.
  5. Photos -- drag-and-drop upload, up to six images, with SEO bio tips built right into the interface.
  6. Verification + payment + submit -- Stripe Identity (ID document plus selfie match), subscription choice, and final submission.

A therapist can start Tuesday and finish Thursday. Progress is there when they come back.

Stripe Identity: Verification Without Storing Sensitive Documents

This was a hard requirement from day one, and one of the trickier technical problems in the build. Stripe Identity handles the ID check: the therapist uploads a government-issued ID, takes a selfie, and Stripe matches the two. The result comes back verified or not. No license files are ever stored on the server.

A required certification checkbox during onboarding shifts licensing liability to the therapist. They confirm they hold a valid Florida massage license. The directory checks identity, not licensure. That's an important legal line, and the client's attorney insisted it be documented clearly.

This same identity-first approach works well across the wider massage and spa directory space. The line between checking identity and checking licensure needs to be legally solid from day one. Stripe does the hard part. The directory just manages the process rather than rebuilding it.

The Pending-Approval System (and Why Old Data Stays Live)

This design choice holds up well and would be used again on future builds.

When a therapist edits their profile (a new photo, an updated bio, changed pricing) the changes don't go live right away. They land in the admin's pending-approval queue. The old profile stays live and visible to consumers the whole time the edit waits for review.

Why does this matter? If a therapist updates their hours at 10pm on a Saturday, they shouldn't vanish from search results until the owner checks the queue on Monday morning. The older version of their data keeps serving visitors in the meantime. The owner sees the changes, clicks approve or reject, and can add a reason if needed. If rejected, Resend sends an email with the reason and a direct link back to the edit screen. No guesswork, no support ticket needed.

New signups work the same way. The profile hits the queue, the owner reviews it, and one click finishes the job.

To stop things from sitting unnoticed, a daily digest email fires at 9am if anything has waited more than 48 hours. Nothing slips through the cracks.

Founding-30 Pricing: Auto-Switching Tiers

Payment runs on two membership tiers. Basic at $29/mo gets a live profile with phone, SMS, and WhatsApp contact links. Premium at $49/mo adds clickable social links (Instagram, Facebook, TikTok, personal website) plus a premium badge on the listing.

The twist is a "Founding 30" rule. The first thirty therapists lock in those rates for good. Once the count hits 30, Stripe subscriptions for new signups switch to higher-tier pricing on their own. No manual flipping, no remembering to update a setting somewhere. The system checks the count at signup and applies the right Stripe price ID. The logic has worked cleanly so far, with no edge cases.

Programmatic SEO: City Pages, Service Pages, and Schema

This is where the directory website development approach pays off over time. Every city with at least one approved therapist gets its own page: /therapists/miami, /therapists/fort-lauderdale, /therapists/tampa, and so on. There are currently 15-20 city pages. Every massage type in the taxonomy gets a service page too, such as /services/deep-tissue-massage and /services/swedish.

All of these pages are built straight from the database. Nobody builds them by hand. When a new therapist gets approved in Sarasota, the Sarasota city page either appears for the first time or updates with their listing, with zero manual work.

Every relevant page carries structured data: LocalBusiness, Service, Person, and FAQPage schema. Static generation means every page ships as plain HTML. Pages load fast across Vercel's edge network, and Core Web Vitals stay in the green. An auto-generated XML sitemap goes to Google Search Console, and conversion tracking fires on signups.

This same programmatic pattern (pages built straight from the database rather than by hand) needs careful crawl-budget management to keep thin pages out of the index. That mix is how larger listings platforms stay indexed cleanly as they grow. Search engines respond well to pages that are real HTML with structured data and fast loads. The pattern also scales without extra work: when the directory reaches 200 therapists across 40 cities, those pages just show up.

The AI Chatbot: Zero-Ticket Support

The chatbot handles FAQ, password resets, profile-editing help, and tier comparison questions. It's trained on the site's real content and policies. The goal is simple: the owner should never have to answer "how do I update my photo" or "what's the difference between Basic and Premium."

It won't replace the approval queue, since that step needs human judgment. For everything that isn't a judgment call, though, the chatbot is the first and usually the last line of support.

Five Weeks, One Thesis

Five weeks, fixed scope. The thesis held: a directory that runs itself. The owner reviews approvals, and the system handles onboarding, verification, payments, emails, SEO pages, and support. That's the goal behind the whole build: respect the operator's time in every small decision, not just in theory.

FAQ

What does it cost to build a directory like this?

It depends on scope, but a verified directory with self-serve onboarding, Stripe Identity, subscription billing, programmatic SEO, and an admin approval system is a big build. Expect it to be a real investment, not a weekend project. Running costs afterward are roughly $20-45/mo.

How long does a project like this take?

This build took five weeks with a fixed scope. Timelines shift with complexity. A directory with identity checks, payment processing, and programmatic city and service pages usually takes five to eight weeks, as long as the scope stays locked from the start.

Can therapists sign up and onboard themselves?

Yes, fully. A six-step wizard walks therapists through profile creation, service picks, pricing, availability, photo uploads, identity checks, and payment. Progress saves at every step, so a half-finished signup can be picked up later. The owner only steps in to approve or reject the finished submission.

How is identity verification handled?

Stripe Identity checks the document and matches it to a selfie during onboarding, confirming the person behind the profile without storing any ID files or sensitive documents on the server. A separate certification checkbox shifts licensing liability to the therapist, who confirms they hold a valid Florida massage license.

Who owns the code and data after the project?

The client owns everything after handoff. The GitHub repository moves to their account, the Supabase project runs under their own login, and the database can be exported as SQL or CSV anytime. It's a standard Next.js stack with no built-in lock-in and no need for our infrastructure.