Phase 18 — Public Business Profile Pages
Every tenant automatically gets a public-facing mini-website at /p/{slug} — like Google My Business + Practo + Fresha, but AI-powered and live-synced with their KaiVox dashboard.
What It Does
The public profile is a standalone web page that customers (end-users of the business) can visit to see business details, view services, read FAQs from the Knowledge Brain, see staff/doctors, check opening hours, read reviews, and initiate contact. The profile is automatically created when first visited — no manual setup required by the tenant.
Profile URL
https://kaivoxai.com/p/{slug}Example:
https://kaivoxai.com/p/citycareclinicNo subdomains, no custom domains in v1 — one consistent format.
Key Routes
| URL | Auth? | Description |
|---|---|---|
GET /p/{slug} | ❌ None | Public profile page — no login required for visitors |
GET /settings/profile | ✅ Auth | Profile settings editor (tenant dashboard) |
POST /settings/profile | ✅ Auth | Save profile settings |
DELETE /settings/profile/cover | ✅ Auth | Remove cover photo |
POST /settings/profile/reviews | ✅ Auth | Add a manual review |
POST /settings/profile/reviews/{id}/approve | ✅ Auth | Approve a review for display |
DELETE /settings/profile/reviews/{id} | ✅ Auth | Delete a review |
Key Files
| Type | Path |
|---|---|
| Controller | app/Http/Controllers/PublicProfileController.php — GET /p/{slug} |
| Controller | app/Http/Controllers/BusinessProfileController.php — settings CRUD |
| Model | app/Models/BusinessProfile.php |
| Model | app/Models/Review.php |
| Table | business_profiles — tenant_id, cover_photo, gallery (JSON), social_links (JSON), seo_title, seo_description, is_public, announcement_text, announcement_expires_at, whatsapp_number, website_url, maps_embed_url |
| Table | reviews — tenant_id, customer_id, rating (1–5), body, reviewer_name, is_verified, is_approved, source (manual/post_call) |
| View | resources/views/public/profile.blade.php — self-contained HTML (no layouts.app) |
| View | resources/views/settings/profile.blade.php — tenant settings tab |
Public Profile Sections
| Section | Data Source |
|---|---|
| Sticky topbar — Call AI + Book Now CTAs | Tenant phone number |
| Cover photo + brand color | business_profiles.cover_photo, CSS custom property |
| Open/Closed status dot | Working hours (live check at render time) |
| Contact row (phone, WhatsApp, email, website, address) | Tenant + business_profiles |
| Social links (FB, IG, X, LinkedIn, YouTube) | business_profiles.social_links JSON |
| Announcement banner (auto-hides when expired) | business_profiles.announcement_* |
| Services catalog (duration + price) | services table (active only) |
| Staff/team grid with "Book" button | staff table (active only) |
| Industry-specific sections | Tenant's active industry pack slug |
| FAQ accordion | knowledge_items where category = faq (active only) |
| Reviews with star ratings | reviews where is_approved = true |
| Google Maps embed | business_profiles.maps_embed_url |
| Opening hours (today highlighted) | working_hours table |
| Upcoming closures | holidays table (next 30 days) |
| KaiVox powered-by badge | Always shown (branding) |
Industry-Specific Sections
Automatically shown based on the tenant's active industry pack:
| Pack | Extra Section |
|---|---|
| medical / dental | Emergency contact banner ("Dental emergency? Call now") |
| mental_health | Crisis protocol section with iCall/Vandrevala helplines, confidentiality note |
| legal | "Free consultation available" badge on attorney cards |
| fitness | "Join free trial" CTA button |
| home_services | Emergency Callout button highlighted prominently |
Profile Settings (Tenant Editor)
Tenants configure their public profile at Settings → Public Profile:
- Profile URL banner with copy-link button and live/hidden badge
- Visibility toggle (public/hidden)
- Cover photo upload (stored in
storage/app/public/covers/) + remove - Tagline and description
- Contact links: WhatsApp number, website URL
- Social links: Facebook, Instagram, X, LinkedIn, YouTube
- Announcement banner with expiry datetime
- SEO title and meta description
- Google Maps embed URL
- Reviews management sidebar: add manual review, approve/hide, delete
Auto-Create Profile
When PublicProfileController receives a request for /p/{slug} and the tenant has no business_profiles record, it creates a default one with is_public = true automatically. Tenants do not need to explicitly create their profile — it exists from day one.
What's Complete
GET /p/{slug}public profile page — no auth required- Auto-creates BusinessProfile if missing on first visit
- Self-contained view (no layouts.app dependency)
- Dynamic brand color from tenant via CSS custom property
- All profile sections wired to live tenant data
- Industry-specific sections (emergency, crisis, consult, trial, callout)
- FAQ accordion from Knowledge Brain (category=faq)
- Reviews with approval workflow
- Profile settings editor in tenant dashboard
- Visibility toggle (public/hidden)
- "Public Profile" nav item in sidebar (Settings section)
- BusinessProfile model with accessors: cover_photo_url, gallery_urls, has_active_announcement
- Review model with accessors: display_name, stars_html, source_label
What's Deferred
- Gallery photo upload (multiple images)
- Real-time booking widget (embedded slot picker without calling)
- "Leave your number" missed-call lead capture form
- Automatic review requests post-call via SMS link
- Profile analytics (view count, call clicks, booking clicks)
- Google Business Review import