Plans
RankFlow AI — Directory Profile System: Complete Plan
Document: Directory Profile Creation & Management System
docs/directory-profile-plan.mdOn this page
- What Changed (vs. Landing Page System)
- 1. Profile Content Collection During Onboarding
- The Problem
- The Solution: Structured Content Collection
- 2. AI Content Generation Pipeline
- Step 1: Bio Generation (300–500 words)
- Step 2: Service Descriptions (50–100 words each)
- Step 3: FAQ Generation (10 Q&A pairs)
- Step 4: Schema Generation (JSON-LD)
- Step 5: Content Compliance Filter (MCI Check)
- 3. Profile Approval Gate
- The Problem
- The Solution: Three-Option Approval
- Approval Options
- Approval Flow
- Notification Sequence
- 4. Profile Publishing Workflow
- Onboarding Steps (Revised)
- Profile Publish Checklist
- 5. Monthly Content Refresh
- What Gets Refreshed
- Refresh Workflow
- Client Approval for Refresh
- 6. Client Dashboard Integration
- /dashboard/profile — Profile Management
- Profile Editing (Limited)
- 7. Fallback Mechanisms
- Scenario 1: Client Already Has a Website
- Scenario 2: Client Wants to Leave
- 8. Technical Implementation
- Database Schema (Prisma)
- Key API Endpoints (tRPC)
- Content Generation Job (Inngest)
- ISR Revalidation
- 9. Cost Analysis
- Content Generation Cost (Per Profile)
- Monthly Refresh Cost (Per Profile)
- At Scale (500 clients)
- 10. Success Metrics
- Profile Quality Metrics
- SEO Metrics
- 11. Summary
Document: Directory Profile Creation & Management System
Version: 1.0.0
Date: 2026-06-13
Status: Post-Pivot (replaces individual landing page / subdomain model)
Scope: Complete workflow for creating, approving, publishing, and refreshing directory profile pages for each client.
What Changed (vs. Landing Page System)#
| Before (Landing Page) | After (Directory Profile) |
|---|---|
Individual subdomain (client.rankflow.in) |
Profile on unified directory (directory.com/clinics/city/client) |
| 3-5 landing page templates | 1-2 profile layouts (standard + premium) |
| Custom domain support (CNAME) | No custom domains — all profiles on directory domain |
| Site editor (AI chat-based editing) | Doctor approval gate (content queued for approval) |
| Static Site Generation (SSG) per client | ISR on dynamic route (/clinics/[city]/[slug]) |
Wildcard DNS (*.rankflow.in) |
Single domain DNS (directory.com) |
| Page traffic analytics | Profile view analytics + click-to-call tracking |
| "Page offline if you cancel" | Profile removed after 30-day grace period |
| Content: hero, services, FAQ, map, reviews | Content: hero, bio, services, FAQ, reviews, map, contact, related clinics |
What was removed:
- ❌ Subdomain assignment
- ❌ Custom domain setup
- ❌ Site editor (AI chat-based website editing)
- ❌ Landing page templates (3-5 variants)
- ❌ A/B testing for landing pages
- ❌ Wildcard DNS management
- ❌ SSG per-client build pipeline
- ❌ "Ownership lock-in" retention model
What was added:
- ✅ Directory profile creation (
/clinics/[city]/[slug]) - ✅ Profile approval gate (doctor reviews AI content before publish)
- ✅ Related clinics (internal linking for SEO juice)
- ✅ City pages (SEO-optimized, unique content per city)
- ✅ Specialty pages (SEO-optimized, unique content per specialty)
- ✅ Directory homepage (search, featured, categories)
- ✅ Profile view analytics
- ✅ 30-day grace period on cancellation
1. Profile Content Collection During Onboarding#
The Problem#
The onboarding form currently collects business info but doesn't explicitly collect the depth of content needed for a rich directory profile. A generic profile (just name + address + phone) won't rank. We need 500+ words of unique content per profile.
The Solution: Structured Content Collection#
Step 2b of onboarding (after basic business info): Expanded content collection with smart defaults and AI suggestions.
Fields to Collect (with AI-suggested defaults)#
| Field | Required | AI Suggestion | Client Can Edit |
|---|---|---|---|
| Business name | ✅ | — | ✅ |
| Specialty | ✅ | Dropdown (30 specialties) | ✅ |
| City | ✅ | Dropdown (500+ Indian cities) | ✅ |
| Years of experience | ❌ | "Enter years" | ✅ |
| Services | ✅ | AI suggests 5-10 based on specialty | ✅ |
| Unique selling points (USPs) | ❌ | AI suggests 3-5 based on specialty + city | ✅ |
| Education/qualifications | ❌ | "Enter degrees" | ✅ |
| Languages spoken | ❌ | AI suggests based on city | ✅ |
| Patient types | ❌ | AI suggests (children, adults, seniors, families) | ✅ |
| Insurance accepted | ❌ | AI suggests common insurers in city | ✅ |
| Photo | ❌ | AI: "Upload a photo of yourself or your clinic" | ✅ |
| Logo | ❌ | AI: "Upload your clinic logo" | ✅ |
| Clinic photos (3-5) | ❌ | AI: "Upload 3-5 photos of your clinic" | ✅ |
| Hours | ✅ | AI suggests standard hours | ✅ |
| Payment methods | ❌ | AI suggests (cash, UPI, card, insurance) | ✅ |
AI Suggestion Logic:
// Pseudo-code for AI suggestion
function suggestServices(specialty: string, city: string): string[] {
const serviceMap = {
"dentist": ["Root Canal Treatment", "Dental Implants", "Teeth Whitening", "Braces & Orthodontics", "Dental Crowns", "Pediatric Dentistry"],
"cardiologist": ["ECG & Echocardiography", "Heart Health Checkups", "Hypertension Management", "Chest Pain Evaluation", "Cardiac Risk Assessment"],
// ... 30 specialties
};
return serviceMap[specialty] || ["General Consultation", "Health Checkup", "Diagnostic Services"];
}
function suggestUSPs(specialty: string, city: string): string[] {
const uspTemplates = [
"Over {years} years of experience in {specialty}",
"Advanced diagnostic equipment for accurate {specialty} care",
"Personalized treatment plans for every patient",
"Accepts all major health insurance in {city}",
"Same-day appointments available",
" multilingual staff (speaks {languages})"
];
// AI picks 3-5 most relevant
}
UI Design:
┌──────────────────────────────────────────────────────┐
│ Step 2: Tell Us About Your Clinic │
│ │
│ Specialty * [ Dentist ▼ ] │
│ │
│ Services (AI-suggested) │
│ ☑ Root Canal Treatment │
│ ☑ Dental Implants │
│ ☑ Teeth Whitening │
│ ☐ Braces & Orthodontics [ + Add Custom ] │
│ │
│ Years of Experience [ 15 ] years │
│ │
│ What Makes You Special? (AI-suggested) │
│ ☑ Over 15 years of experience in dentistry │
│ ☑ Advanced diagnostic equipment │
│ ☑ Same-day appointments available │
│ [ + Add Your Own ] │
│ │
│ [ Back ] [ Save & Continue → ] │
└──────────────────────────────────────────────────────┘
Key principle: AI suggests. Client edits. Doctor approves. Never publish without approval.
2. AI Content Generation Pipeline#
Step 1: Bio Generation (300–500 words)#
Input: Business name, specialty, city, services, USPs, years of experience, education, languages, patient types
Output: Professional, MCI-compliant bio
AI Prompt:
You are a medical content writer for an Indian healthcare directory.
Write a 300-500 word professional bio for {businessName}, a {specialty} in {city}.
Constraints:
- NO promotional claims (no "best", "top", "most trusted", "leading")
- NO guaranteed outcomes (no "100% success", "painless", "immediate results")
- Informational tone only — describe services, experience, and patient care approach
- Include: specialty, city, years of experience, key services, patient types, languages
- Mention MCI registration implicitly (e.g., "registered medical practitioner")
- End with a call to action: "Schedule a consultation" or "Book an appointment"
Structure:
1. Opening paragraph (2-3 sentences): Who they are, specialty, city, experience
2. Services paragraph: 3-5 key services with brief descriptions
3. Patient care philosophy: Approach to patient care, patient types
4. Closing: Languages, hours, booking CTA
Output: Plain text, no markdown, no HTML.
Example Output:
Dr. Smith Dental Clinic is a dental practice located in Kochi, Kerala, serving patients of all ages for over 15 years. The clinic provides comprehensive dental care including root canal treatment, dental implants, teeth whitening, and orthodontic services.
The clinic offers a range of services designed to address common dental concerns. Root canal treatment is performed using modern techniques to preserve natural teeth. Dental implant procedures help restore missing teeth with permanent solutions. Teeth whitening services are available for patients seeking cosmetic improvements. Pediatric dentistry services cater to children and adolescents.
The practice focuses on patient comfort and preventive care. The team believes in educating patients about oral health to prevent future dental issues. Same-day appointments are available for urgent dental concerns. The clinic accepts all major health insurance providers in Kochi.
Dr. Smith and the team speak English, Malayalam, and Hindi. The clinic is open Monday through Saturday, 9:00 AM to 7:00 PM. Patients can schedule appointments by phone or WhatsApp.
Cost: ~$0.03 per bio (Claude Haiku, ~400 words)
Step 2: Service Descriptions (50–100 words each)#
Input: Service name, specialty, city
Output: Brief description of the service
AI Prompt:
Describe the dental service "{serviceName}" in 50-100 words for a patient in {city}.
Constraints: Informational only. No promotional language. No pricing (unless asked). Include what the service involves and who it's for.
Example Output (Root Canal Treatment):
Root canal treatment is a dental procedure used to treat infected or damaged tooth pulp. The procedure involves removing the infected tissue, cleaning the root canal, and sealing the tooth to prevent further infection. This treatment helps save the natural tooth and eliminates pain caused by the infection. It is recommended for patients with deep cavities, cracked teeth, or persistent tooth pain.
Cost: ~$0.01 per service (Claude Haiku, ~75 words)
Step 3: FAQ Generation (10 Q&A pairs)#
Input: Specialty, city, services, common patient questions
Output: 10 Q&A pairs
AI Prompt:
Generate 10 FAQ questions that patients in {city} commonly ask about {specialty} services.
For each question, provide a 2-3 sentence informational answer.
Constraints:
- Questions should be practical and patient-focused
- Answers should be informational, not promotional
- Include cost-related questions where appropriate (give ranges, not exact prices)
- Include questions about insurance, appointment booking, and first visits
- No "best doctor" or "why choose us" questions
Output format:
Q1: [Question]
A1: [Answer]
...
Q10: [Question]
A10: [Answer]
Example Output:
Q1: What is the cost of root canal treatment in Kochi?
A1: The cost of root canal treatment in Kochi typically ranges from ₹3,000 to ₹8,000 depending on the tooth and complexity. Molars generally cost more than front teeth. Most dental insurance plans cover a portion of the cost.
Q2: How long does a dental implant procedure take?
A2: A dental implant procedure typically takes 3-6 months from start to finish. The initial implant placement takes 1-2 hours, followed by a healing period of 2-3 months. The final crown is placed after the implant has integrated with the jawbone.
Q3: Does teeth whitening damage enamel?
A3: Professional teeth whitening performed by a dentist is safe and does not damage enamel. The bleaching agents are carefully applied to minimize sensitivity. Some patients may experience temporary sensitivity for 24-48 hours after the procedure.
... (7 more)
Cost: ~$0.05 per FAQ set (Claude Haiku, ~10 Q&A pairs)
Step 4: Schema Generation (JSON-LD)#
Input: All profile data (name, address, phone, hours, services, reviews, FAQs)
Output: JSON-LD schema object
No AI needed — structured data generation via code:
function generateSchema(profile: DirectoryProfile): JsonLdObject {
return {
"@context": "https://schema.org",
"@graph": [
{
"@type": "LocalBusiness",
"@id": `${profile.canonicalUrl}#business`,
"name": profile.businessName,
"description": profile.bio.substring(0, 300),
"url": profile.canonicalUrl,
"telephone": profile.phone,
"email": profile.email,
"address": {
"@type": "PostalAddress",
"streetAddress": profile.address,
"addressLocality": profile.city,
"addressRegion": profile.state,
"addressCountry": "IN"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": profile.latitude,
"longitude": profile.longitude
},
"openingHoursSpecification": profile.hours.map(h => ({
"@type": "OpeningHoursSpecification",
"dayOfWeek": h.day,
"opens": h.opens,
"closes": h.closes
})),
"image": profile.photoUrl,
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": profile.avgRating,
"reviewCount": profile.reviewCount
},
"hasMap": profile.mapUrl,
"priceRange": "₹₹"
},
{
"@type": "Physician",
"@id": `${profile.canonicalUrl}#physician`,
"name": profile.doctorName,
"medicalSpecialty": profile.specialty,
"worksFor": { "@id": `${profile.canonicalUrl}#business` }
},
{
"@type": "FAQPage",
"mainEntity": profile.faqs.map(faq => ({
"@type": "Question",
"name": faq.question,
"acceptedAnswer": { "@type": "Answer", "text": faq.answer }
}))
},
{
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://directory.com/" },
{ "@type": "ListItem", "position": 2, "name": `Clinics in ${profile.city}`, "item": `https://directory.com/clinics/${profile.citySlug}` },
{ "@type": "ListItem", "position": 3, "name": profile.businessName, "item": profile.canonicalUrl }
]
}
]
};
}
Cost: $0 (code generation)
Step 5: Content Compliance Filter (MCI Check)#
Before any content is shown to the doctor for approval, it passes through a compliance filter:
const FORBIDDEN_WORDS = [
"best", "top", "most trusted", "leading", "no.1", "number one",
"guaranteed", "100% success", "painless", "immediate results",
"miracle", "cure all", "magic", "secret"
];
const FORBIDDEN_PATTERNS = [
/best\s+(dentist|doctor|clinic|surgeon)/i,
/top\s+(dentist|doctor|clinic|surgeon)/i,
/most\s+(trusted|experienced|qualified)/i,
/\d+%\s+(success|guarantee)/i,
/guaranteed\s+(results|cure|outcome)/i,
];
function checkCompliance(content: string): ComplianceResult {
const violations = [];
FORBIDDEN_WORDS.forEach(word => {
if (content.toLowerCase().includes(word)) {
violations.push({ type: "FORBIDDEN_WORD", word, severity: "ERROR" });
}
});
FORBIDDEN_PATTERNS.forEach(pattern => {
if (pattern.test(content)) {
violations.push({ type: "FORBIDDEN_PATTERN", pattern: pattern.source, severity: "ERROR" });
}
});
// Check for medical disclaimers
if (!content.includes("This information is for educational purposes")) {
violations.push({ type: "MISSING_DISCLAIMER", severity: "WARNING" });
}
return { passed: violations.filter(v => v.severity === "ERROR").length === 0, violations };
}
If compliance fails: Content is sent back to AI with instructions: "Rewrite to remove promotional claims. Use informational tone only."
3. Profile Approval Gate#
The Problem#
Clients (doctors) want to review content before it goes live. But they don't want to spend hours editing. They want to "approve with minor tweaks" or "reject with feedback."
The Solution: Three-Option Approval#
┌──────────────────────────────────────────────────────┐
│ 📝 Review Your Directory Profile │
│ │
│ Your AI Copilot has generated your profile content. │
│ Review and approve before it goes live. │
│ │
│ ── Bio ────────────────────────────────────────────│
│ Dr. Smith Dental Clinic is a dental practice... │
│ [ Edit Bio ] │
│ │
│ ── Services ───────────────────────────────────────│
│ ☑ Root Canal Treatment │
│ ☑ Dental Implants │
│ ☑ Teeth Whitening │
│ [ Edit Services ] │
│ │
│ ── FAQs ────────────────────────────────────────────│
│ Q: What is the cost of root canal treatment? │
│ A: The cost typically ranges from ₹3,000... │
│ [ Edit FAQs ] │
│ │
│ [ ❌ Reject & Request Changes ] │
│ [ ✏️ Approve with Edits ] │
│ [ ✅ Approve as Is ] │
│ │
│ ⏰ Auto-approve in 48 hours if no action │
└──────────────────────────────────────────────────────┘
Approval Options#
| Option | Action | Timeline |
|---|---|---|
| ✅ Approve as Is | Profile goes live immediately | Instant |
| ✏️ Approve with Edits | Client makes edits, then approves | Within 24 hours |
| ❌ Reject & Request Changes | Client provides feedback, AI regenerates | 24-48 hours |
| Auto-approve | If no action within 48 hours, profile auto-publishes | 48 hours |
Approval Flow#
AI generates content → Compliance check →
→ Doctor reviews →
→ [Approve] → Publish immediately
→ [Edit + Approve] → Save edits → Publish
→ [Reject] → Collect feedback → AI regenerates → Loop
→ [No action] → Auto-approve after 48 hours
Notification Sequence#
| Time | Channel | Message |
|---|---|---|
| T+0 (content ready) | WhatsApp + Email | "Your directory profile is ready for review. Approve here: [link]" |
| T+24h (no action) | WhatsApp reminder | "Reminder: Your profile is ready for approval. Auto-publishes in 24 hours." |
| T+48h (auto-approve) | "Your profile has been published on India's trusted healthcare directory. View: [link]" | |
| T+48h (rejected) | WhatsApp + Email | "Your feedback has been received. AI is regenerating your profile. New draft in 2 hours." |
4. Profile Publishing Workflow#
Onboarding Steps (Revised)#
Step 1: Business Info [C] → Name, category, address, phone, hours
Step 2: Content Collection [C] → Services, USPs, experience, photos, media
Step 3: Consent & Compliance [C] → DPDPA consent, MCI acknowledgment
Step 4: Payment [C] → Plan selection, payment, trial start
Step 5: Directory Profile Creation [S] → AI generates bio, services, FAQs, schema
Step 6: Doctor Approval [C] → Review profile content, approve/edit/reject
Step 7: Profile Publish [S] → Publish on directory, update GBP, submit citations
Step 8: GBP Connect [C] → OAuth connect, sync data
Step 9: Social Connect [C] → Instagram, Facebook connect
Step 10: Citations [S] → Submit 30 directory listings
Step 11: First Posts [S] → First GBP post + social posts
Step 12: Welcome [S] → WhatsApp + email welcome report
[C] = Client action | [S] = System automated
Profile Publish Checklist#
Before a profile goes live, the system verifies:
- Content passes MCI compliance check
- Doctor approval received (or auto-approve timer expired)
- All required fields populated (name, address, phone, hours, bio, services)
- At least 1 photo uploaded (or placeholder used)
- Schema markup is valid JSON-LD
- Meta title and description are within limits (70 chars, 160 chars)
- URL slug is unique within city
- City page exists (or is created)
- GBP
websiteUriis updated to directory profile URL - XML sitemap is updated
- ISR revalidation is triggered
5. Monthly Content Refresh#
What Gets Refreshed#
| Content | Refresh Frequency | Method |
|---|---|---|
| Bio | Monthly | AI rewrites with same constraints |
| 3 FAQs | Monthly | AI generates new questions based on trends |
| Service descriptions | Monthly | AI refreshes 2-3 services |
| Reviews | Real-time | Google review sync + new review replies |
| Hours | On change | Client updates via dashboard |
| Photos | On change | Client uploads via dashboard |
| Schema | On content change | Regenerated automatically |
Refresh Workflow#
1st of every month (Inngest cron job):
→ Fetch all PUBLISHED profiles
→ For each profile:
a. Generate new bio (Claude Haiku, ~400 words)
b. Pick 3 services to refresh
c. Generate 3 new FAQs (replace oldest 3)
d. Regenerate schema if content changed
e. Set status to PENDING_REVIEW (if plan requires approval)
f. Send WhatsApp: "Your profile content has been refreshed. Review changes: [link]"
g. Trigger ISR revalidation after approval
→ Log all refreshes
→ Send admin report
Client Approval for Refresh#
| Plan | Refresh Approval |
|---|---|
| Starter | Auto-approve (no doctor review) |
| Standard | Doctor reviews changes, approves in 24h |
| Premium | Doctor reviews changes, approves in 24h |
| Enterprise | Doctor reviews changes, approves in 24h + custom content |
6. Client Dashboard Integration#
/dashboard/profile — Profile Management#
┌──────────────────────────────────────────────────────┐
│ 📋 My Directory Profile │
│ │
│ Status: ✅ LIVE │
│ URL: directory.com/clinics/kochi/dr-smith-dental │
│ Views: 1,247 (last 30 days) │
│ Click-to-Call: 89 (last 30 days) │
│ │
│ [ 👁️ View Live Profile ] [ 📋 Edit Content ] │
│ │
│ ── Content Status ─────────────────────────────────│
│ Bio ✅ Published (refreshed 2 days ago) │
│ Services ✅ Published (8 services) │
│ FAQs ✅ Published (10 FAQs) │
│ Reviews ✅ 47 Google reviews synced │
│ Photos ✅ 5 photos uploaded │
│ │
│ ── Pending Changes ─────────────────────────────────│
│ 📝 New bio draft waiting for approval │
│ [ Review Changes ] │
│ │
│ ── GBP Link ────────────────────────────────────────│
│ Google Business Profile linked ✅ │
│ Website URL: directory.com/clinics/kochi/... │
│ [ Update GBP ] │
│ │
│ ── Share Profile ────────────────────────────────────│
│ [ Copy Link ] [ Share on WhatsApp ] [ QR Code ] │
└──────────────────────────────────────────────────────┘
Profile Editing (Limited)#
Clients can edit:
- ✅ Contact info (phone, hours, address)
- ✅ Photos (upload, replace, reorder)
- ✅ Services (add, remove, reorder)
- ✅ Bio (edit with compliance check)
- ✅ FAQs (edit, add, remove)
Clients CANNOT edit:
- ❌ URL slug (SEO-critical, fixed after publish)
- ❌ City (would change the URL structure)
- ❌ Schema markup (auto-generated)
- ❌ Meta tags (auto-generated)
- ❌ Related clinics (auto-generated)
7. Fallback Mechanisms#
Scenario 1: Client Already Has a Website#
Old behavior: Offered to host their landing page on our subdomain or inject schema into their existing site.
New behavior: No change needed. The directory profile is SEPARATE from their existing website. They keep their website. We create a directory profile. Their GBP can link to EITHER (or both — but we recommend the directory profile for higher DA).
Client: "I already have a website: drsmith.com"
RankFlow: "Great! Your existing website stays as-is. We'll also create a premium directory profile for you on India's trusted healthcare directory. Your Google Business Profile will link to the directory profile for better rankings. You can keep both."
Scenario 2: Client Wants to Leave#
Old behavior: "Page goes offline, citations deleted, rankings collapse."
New behavior:
Day 0: Client cancels
Day 1: Profile status → PAUSED (still visible but marked "listing no longer active")
Day 7: WhatsApp reminder: "Your profile will be removed in 23 days. Reconnect to keep it live."
Day 30: Profile status → ARCHIVED (returns 404, removed from sitemap)
Day 30: Citations on external directories (Justdial, Practo, etc.) → Gradually removed or updated
Day 30: GBP website URL → Client can update to their own website
Key difference: The directory profile is removed, but the external citations may remain (depending on directory policies). The client loses the directory authority but retains their GBP and any citations that don't require ongoing maintenance.
8. Technical Implementation#
Database Schema (Prisma)#
See directory-website-architecture.md Section 5 for the complete DirectoryProfile schema.
Key API Endpoints (tRPC)#
See directory-website-architecture.md Section 6 for the complete API specification.
Content Generation Job (Inngest)#
// src/jobs/profile-content-generation.ts
import { inngest } from "@/lib/inngest";
export const generateProfileContent = inngest.createFunction(
{ id: "generate-profile-content" },
{ event: "profile/content.generate" },
async ({ event, step }) => {
const { practiceId } = event.data;
// Step 1: Fetch practice data
const practice = await step.run("fetch-practice", async () => {
return db.practice.findUnique({ where: { id: practiceId } });
});
// Step 2: Generate bio
const bio = await step.run("generate-bio", async () => {
return ai.generate({
task: "directory_profile_bio",
model: "claude-sonnet",
input: { practice },
});
});
// Step 3: Generate service descriptions
const services = await step.run("generate-services", async () => {
return Promise.all(practice.services.map(service =>
ai.generate({ task: "directory_profile_service", model: "claude-haiku", input: { service } })
));
});
// Step 4: Generate FAQs
const faqs = await step.run("generate-faqs", async () => {
return ai.generate({
task: "directory_profile_faq",
model: "claude-sonnet",
input: { practice },
});
});
// Step 5: Compliance check
const compliance = await step.run("check-compliance", async () => {
return checkCompliance(bio + services.join(" ") + faqs.join(" "));
});
if (!compliance.passed) {
// Retry with feedback
await step.run("retry-with-feedback", async () => {
return ai.generate({
task: "directory_profile_bio",
model: "claude-sonnet",
input: { practice, feedback: compliance.violations },
});
});
}
// Step 6: Save to database
await step.run("save-profile", async () => {
return db.directoryProfile.create({
data: {
practiceId,
status: "PENDING_REVIEW",
bio,
services: { create: services },
faqs: { create: faqs },
schemaMarkup: generateSchema({ practice, bio, services, faqs }),
},
});
});
// Step 7: Send approval notification
await step.run("send-approval-request", async () => {
return sendWhatsApp({
to: practice.phone,
message: `Your directory profile is ready for review. Approve here: ${approvalUrl}`,
});
});
return { profileId, status: "PENDING_REVIEW" };
}
);
ISR Revalidation#
// src/app/api/revalidate/route.ts
import { revalidatePath } from "next/cache";
export async function POST(request: Request) {
const { profileId, citySlug, slug } = await request.json();
// Revalidate the profile page
revalidatePath(`/clinics/${citySlug}/${slug}`);
// Revalidate the city page
revalidatePath(`/clinics/${citySlug}`);
// Revalidate the specialty page (if applicable)
// revalidatePath(`/specialty/${specialty}`);
return Response.json({ revalidated: true, profileId });
}
9. Cost Analysis#
Content Generation Cost (Per Profile)#
| Content | Model | Cost | Quantity | Total |
|---|---|---|---|---|
| Bio | Claude Sonnet | ~$0.03 | 1 | $0.03 |
| Service descriptions | Claude Haiku | ~$0.01 | 8 | $0.08 |
| FAQs | Claude Sonnet | ~$0.05 | 1 set | $0.05 |
| Schema | Code | $0 | 1 | $0 |
| City page content | Claude Sonnet | ~$0.02 | 1 | $0.02 |
| Total per profile | ~$0.18 |
Monthly Refresh Cost (Per Profile)#
| Content | Cost | Quantity | Total |
|---|---|---|---|
| Bio rewrite | ~$0.02 | 1 | $0.02 |
| Service refresh (3 services) | ~$0.01 | 3 | $0.03 |
| FAQ refresh (3 new) | ~$0.03 | 1 | $0.03 |
| Total per profile/month | ~$0.08 |
At Scale (500 clients)#
| Metric | Monthly Cost |
|---|---|
| Initial content generation (500 profiles) | ~$90 |
| Monthly refresh (500 profiles) | ~$40 |
| Total content cost per month | ~$40 |
| Per client per month | ~$0.08 |
Compare to old model:
- Old model: 500 subdomains × Vercel hosting + 500 SSG builds + wildcard DNS + custom domain support = ~$200-300/month
- New model: 1 domain × Vercel hosting + ISR caching = ~$50-100/month
- Savings: ~$150-200/month at 500 clients
10. Success Metrics#
Profile Quality Metrics#
| Metric | Target | Measurement |
|---|---|---|
| Average bio length | 400+ words | Count words per profile |
| Average services per profile | 5+ | Count services |
| Average FAQs per profile | 10+ | Count FAQs |
| Compliance pass rate | 100% | Automated check on every piece of content |
| Doctor approval rate | 80%+ | Approved / (Approved + Rejected) |
| Average approval time | <24 hours | Time from "content ready" to "approved" |
| Profile view count (month 1) | 50+ per profile | Analytics tracking |
| Profile view count (month 6) | 200+ per profile | Analytics tracking |
SEO Metrics#
| Metric | Target | Measurement |
|---|---|---|
| Directory DA (Month 1) | 10+ | Moz/Ahrefs |
| Directory DA (Month 6) | 25+ | Moz/Ahrefs |
| Directory DA (Month 12) | 35+ | Moz/Ahrefs |
| Profiles indexed by Google | 100% | Google Search Console |
| Average profile ranking (Month 3) | Top 10 for "[specialty] in [city]" | Rank tracking |
| Average profile ranking (Month 6) | Top 5 for "[specialty] in [city]" | Rank tracking |
| Internal links per profile | 3-6 | Related clinics |
| Backlinks to directory (Month 6) | 50+ | Ahrefs/Moz |
| Backlinks to directory (Month 12) | 200+ | Ahrefs/Moz |
11. Summary#
This directory profile system replaces the individual landing page model with a unified, high-authority approach that is:
- Better SEO: One compounding domain vs. 100 zero-authority subdomains
- Simpler tech: No wildcard DNS, no per-client builds, no custom domains
- Better for clients: They get rankings, not a website they don't want
- More maintainable: One codebase, one design system, one SEO strategy
- Future-proof: The directory becomes a standalone, monetizable asset
Key workflows:
- Onboarding collects structured content (Step 2b)
- AI generates bio, services, FAQs, schema (Steps 5-6)
- Doctor approves via three-option gate (Step 6)
- Profile publishes on directory + GBP updates + citations submit (Step 7)
- Monthly refresh regenerates content + doctor re-approves (ongoing)
Next step: Begin development of the directory profile creation pipeline (Week 3-4 of the new architecture timeline).