YeboCars — Product Requirements Document
Buy and sell cars across Africa. The TikTok-style marketplace for vehicles.
Status: 🔄 Building (Rebranding from Bamzu)
Previous Name: Bamzu
Document Version: 1.0
Last Updated: March 19, 2026
Table of Contents
- Vision
- Problem
- Solution
- Core Features
- User Journeys
- Data Models
- API Reference
- Service Architecture
- Authentication
- Billing & Monetization
- Technical Stack
- Gaps & Missing Features
- Roadmap
Vision
YeboCars is the car marketplace for Africa — a mobile-first platform that makes buying and selling vehicles as engaging as scrolling TikTok. One swipe at a time, discover your next car.
The goal: Any African can list a car in 5 minutes, and any buyer can find their perfect vehicle through AI-powered discovery and a frictionless, social-media-inspired browsing experience.
Problem
Current State (African Car Market)
- Fragmented listings: Cars scattered across Facebook groups, WhatsApp, local classifieds
- Trust issues: No verification, high fraud risk, no escrow
- Discovery friction: Static listings, no AI-powered search, poor filtering
- Dealer pain: No professional tools, manual lead management, no analytics
- Cross-border barriers: Different currencies, no multi-country support
Pain Points
- Buyers: Can't find the right car, don't trust listings, tedious search
- Sellers: Hard to reach buyers, no tools to manage listings, no insights
- Dealers: Lack professional dashboard, lead management, subscription tiers
Solution
YeboCars = TikTok Meets Car Shopping
┌─────────────────┐
│ YeboCars │
│ │
│ TikTok Browser │
│ AI Discovery │
│ Dealer Tools │
└────────┬────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Buyers │ │ Dealers │ │ Private │
│ │ │ │ │ Sellers │
│ Swipe cars │ │ Dashboard │ │ Quick list │
│ AI search │ │ Analytics │ │ Chat buyers │
│ Chat seller │ │ Lead mgmt │ │ Track views │
└─────────────┘ └─────────────┘ └─────────────┘Key differentiators:
- TikTok-style vertical car browsing
- AI-powered natural language search ("find me a family SUV under $20k")
- Multi-country support with local currency display
- Dealer subscription tiers with premium features
- In-app messaging and lead management
Core Features
1. TikTok-Style Car Browser
- Full-screen vertical cards — Swipe through cars like stories
- Auto-playing media — Images and videos play automatically
- Quick actions — Like, comment, share, message from card
- Infinite scroll — Algorithm-driven discovery
2. AI-Powered Search & Discovery
- Natural language search — "Find me a fuel-efficient sedan under E200,000"
- Lifestyle quiz — Answer questions, get personalized recommendations
- Vector embeddings — Semantic search on car models
- Price insights — AI-driven market positioning ("Below Market", "At Market")
3. Dual Seller Types
- Dealers — Professional accounts with dashboard, analytics, plans
- Private Sellers — Individual users listing personal vehicles
- Both use the same listing flow, different features unlocked
4. Comprehensive Car Listings
- Make/Model/Year — Seeded database of all major makes/models
- Rich media — Up to 20 images, videos supported
- Detailed specs — Fuel type, transmission, mileage, body type, features
- Pricing — Optional (can list as "Contact for price")
- Country-aware — Auto-assigns country, displays local currency
5. Dealer Dashboard
- Car management — List, edit, mark sold
- Analytics — Views, inquiries, favorites per listing
- Lead management — Track and respond to inquiries
- Export — CSV/JSON export of listings
- Business profile — Hours, website, description
6. Social Interactions
- Likes — Like cars, view your liked cars
- Comments — Comment on listings, reply threads
- Shares — Generate share links with analytics
- Test drive booking — Schedule directly from listing
7. Messaging System
- In-app chat — Message sellers/dealers
- Conversation threads — Full message history
- Read receipts — Delivered, read status
- Car context — Link messages to specific listings
8. Notifications & Alerts
- Price alerts — Notify when car drops to target price
- New car alerts — Custom filters, notify on new matches
- Background jobs — Automated alert checking
9. Multi-Country Support
- Country detection — Auto-detect from phone number or IP
- Local currency — Display prices in user's currency
- African currencies — ZAR, NGN, KES, GHS, SZL, and 15+ more
- Stripe fallback — Charge in USD when local currency unsupported
User Journeys
Journey 1: Buyer Discovers Car via TikTok Browser
1. Opens YeboCars app
2. Lands on TikTok-style browser (default view)
3. Swipes through full-screen car cards
4. Sees a Toyota Camry they like
5. Taps like button → saved to favorites
6. Taps "Contact Dealer" → opens chat
7. Messages dealer about availability
8. Schedules test drive from card
9. Receives confirmation notificationTime: ~2 minutes from browse to test drive booking
Journey 2: Private Seller Lists a Car
1. Opens YeboCars
2. Taps "Sell" or navigates to "Become Seller"
3. If not logged in → Login/Signup flow
4. Phone verification via OTP (WhatsApp)
5. Fills car details:
- Make/Model/Year (dropdown)
- Mileage, condition
- Photos (upload up to 20)
- Price (optional)
- Description
6. Submits listing
7. Listing goes live immediately
8. User sees car in "My Cars" section
9. Gets notified when someone views/messagesTime: ~5 minutes
Journey 3: Dealer Upgrades Plan
1. Dealer logs into YeboCars
2. Navigates to Dashboard
3. Sees "Upgrade Plan" prompt
4. Views available plans:
- Basic: $10/mo - 10 listings
- Dealer: $25/mo - 50 listings
- Pro: $60/mo - Unlimited
5. Selects "Dealer" plan
6. Redirected to Stripe checkout
7. Pays in local currency (ZAR)
8. Plan activated, limits increased
9. New features unlockedJourney 4: AI-Powered Search
1. User taps search/discover
2. Opens Smart Discovery
3. Types: "family SUV under 300000, less than 5 years old"
4. AI parses query:
- bodyType: SUV
- maxPrice: 300000
- minYear: 2021
5. Returns matching cars
6. Shows AI confidence score
7. User browses results
8. Finds match, starts chatData Models
User
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| phone | String | Yes | International format (+26878422613) |
| String | No | Email address | |
| password | String | Yes | Bcrypt hashed password |
| name | String | No | Display name |
| avatar | String | No | Avatar URL |
| countryId | String | No | FK to Country |
| isPhoneVerified | Boolean | Yes | Phone OTP verified |
| role | Enum | Yes | user, dealer, admin |
| dealerId | String | No | FK to Dealer (if dealer role) |
| lastLogin | DateTime | No | Last login timestamp |
| createdAt | DateTime | Yes | Account creation |
| updatedAt | DateTime | Yes | Last update |
Country
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| name | String | Yes | Full name (e.g., "Eswatini") |
| code | String | Yes | ISO 3166-1 alpha-2 (SZ) |
| code3 | String | Yes | ISO 3166-1 alpha-3 (SWZ) |
| callingCode | String | Yes | Phone prefix (+268) |
| region | String | Yes | Continent/region |
| subregion | String | No | Sub-region |
| currency | JSON | Yes | |
| languages | String[] | Yes | Supported languages |
| flag | String | Yes | Flag emoji or URL |
| isActive | Boolean | Yes | Country enabled |
| isSupported | Boolean | Yes | Marketplace supported |
Car
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| make | String | Yes | Toyota, BMW, etc. |
| model | String | Yes | Camry, 3 Series, etc. |
| year | Int | Yes | Manufacturing year |
| price | Float | Yes | Price (0 = Contact for price) |
| mileage | Float | Yes | Kilometers/miles driven |
| fuelType | Enum | Yes | Petrol, Diesel, Electric, Hybrid |
| transmission | Enum | Yes | Manual, Automatic |
| bodyType | String | Yes | Sedan, SUV, Hatchback, etc. |
| color | String | Yes | Primary color |
| exteriorColor | String | No | Detailed exterior |
| interiorColor | String | No | Interior color |
| description | String | No | Free-text description |
| images | String[] | Yes | Array of image URLs |
| videos | String[] | No | Array of video URLs |
| features | String[] | No | Features list |
| sellerId | String | Yes | FK to User |
| sellerType | Enum | Yes | dealer, private |
| condition | Enum | Yes | New, Used, Certified Pre-Owned |
| status | Enum | Yes | ACTIVE, INACTIVE, SOLD, PENDING, REJECTED |
| countryId | String | Yes | FK to Country |
| mediaProcessingStatus | Enum | No | none, pending, processing, completed, failed |
| mediaProcessingData | JSON | No | Processing metadata |
| createdAt | DateTime | Yes | Listing creation |
| updatedAt | DateTime | Yes | Last update |
Indexes: make, model, year, price, mileage, fuelType, transmission, bodyType, sellerId, sellerType, condition, status, countryId, (make, model), (price, year), (status, createdAt)
Dealer
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| name | String | Yes | Business name |
| String | Yes | Business email (unique) | |
| phone | String | Yes | Business phone |
| location | String | Yes | Physical location |
| businessLicense | String | No | License number |
| type | Enum | Yes | dealer, private |
| isApproved | Boolean | Yes | Admin approved |
| rating | Float | No | Average rating (1-5) |
| totalSales | Int | Yes | Completed sales count |
| joinedDate | DateTime | Yes | Account creation |
| businessHours | JSON | No | Map of day → hours |
| website | String | No | Business website |
| description | String | No | Business description |
| businessPhone | String | No | Alt phone number |
| countryId | String | Yes | FK to Country |
| plan | String | Yes | FREE, BASIC, DEALER, PRO |
| planExpiry | DateTime | No | Plan expiration date |
| stripeCustomerId | String | No | Stripe customer ID |
CarMake
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| name | String | Yes | Make name (Toyota) |
| country | String | Yes | Country of origin |
| isLuxury | Boolean | Yes | Premium brand flag |
| isActive | Boolean | Yes | Active in system |
| logoUrl | String | No | Brand logo URL |
CarModel
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| make | String | Yes | Parent make name |
| model | String | Yes | Model name (Camry) |
| year | Int | No | Model year if specific |
| bodyType | String | No | Default body type |
| isActive | Boolean | Yes | Active in system |
| imageUrl | String | No | Model image URL |
| specifications | JSON | No | Nested specs data |
| embedding | Float[] | No | Vector embedding for AI |
CarLike
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| carId | String | Yes | FK to Car |
| userId | String | Yes | FK to User |
| createdAt | DateTime | Yes | Like timestamp |
Unique: (carId, userId)
CarComment
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| carId | String | Yes | FK to Car |
| userId | String | Yes | FK to User |
| comment | String | Yes | Comment text |
| likes | Int | Yes | Like count |
| parentId | String | No | FK to parent comment (replies) |
| createdAt | DateTime | Yes | Comment timestamp |
Favorite
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| userId | String | Yes | FK to User |
| carId | String | Yes | FK to Car |
| createdAt | DateTime | Yes | Favorited timestamp |
Unique: (userId, carId)
Inquiry
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| customerId | String | Yes | FK to User (buyer) |
| carId | String | Yes | FK to Car |
| dealerId | String | Yes | FK to Dealer |
| message | String | Yes | Inquiry message |
| contactMethod | Enum | Yes | whatsapp, phone, email |
| status | Enum | Yes | pending, responded, closed |
| response | String | No | Dealer response |
| respondedAt | DateTime | No | Response timestamp |
TestDrive
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| carId | String | Yes | FK to Car |
| userId | String | Yes | FK to User |
| dealerId | String | Yes | FK to Dealer |
| preferredDate | DateTime | Yes | Requested date |
| preferredTime | String | Yes | Requested time |
| phone | String | Yes | Contact phone |
| String | No | Contact email | |
| status | Enum | Yes | pending, confirmed, completed, cancelled, no_show |
| notes | String | No | Additional notes |
| confirmedDate | DateTime | No | Confirmed date |
| confirmedTime | String | No | Confirmed time |
Message
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| senderId | String | Yes | FK to User |
| receiverId | String | Yes | FK to User |
| content | String | Yes | Message content |
| isRead | Boolean | Yes | Read status |
| messageType | String | No | text, image, document, mixed |
| metadata | JSON | No | Additional data |
| readAt | DateTime | No | Read timestamp |
PriceAlert
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| userId | String | Yes | FK to User |
| carId | String | Yes | FK to Car |
| targetPrice | Float | Yes | Alert threshold |
| isActive | Boolean | Yes | Alert enabled |
NewCarAlert
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| userId | String | Yes | FK to User |
| filters | JSON | Yes | Search filters |
| isActive | Boolean | Yes | Alert enabled |
DealerApplication
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| dealerId | String | Yes | FK to Dealer |
| businessName | String | Yes | Business name |
| businessAddress | String | Yes | Physical address |
| businessPhone | String | Yes | Contact phone |
| businessEmail | String | Yes | Contact email |
| licenseNumber | String | No | Business license |
| taxId | String | No | Tax registration |
| description | String | No | Business description |
| status | String | Yes | pending, approved, rejected |
| reviewedBy | String | No | Admin reviewer ID |
| reviewedAt | DateTime | No | Review timestamp |
| reviewNotes | String | No | Admin notes |
DealerReview
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| dealerId | String | Yes | FK to Dealer |
| userId | String | Yes | FK to User |
| rating | Int | Yes | 1-5 stars |
| review | String | No | Review text |
| isAnonymous | Boolean | Yes | Anonymous flag |
Unique: (dealerId, userId)
Otp
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| String | Yes | Phone number | |
| otp | String | Yes | 6-digit code |
| expiresAt | DateTime | Yes | Expiration time |
| isUsed | Boolean | Yes | Used flag |
| userId | String | Yes | Temp user data or user ID |
BlogPost
| Field | Type | Required | Description |
|---|---|---|---|
| id | String (CUID) | Yes | Unique identifier |
| title | String | Yes | Post title |
| slug | String | Yes | URL slug (unique) |
| content | String | Yes | Post content |
| excerpt | String | No | Short excerpt |
| category | String | No | Post category |
| tags | String[] | Yes | Tag array |
| status | String | Yes | published, draft |
| author | String | Yes | Author name |
| featuredImage | String | No | Hero image URL |
| publishedAt | DateTime | No | Publish date |
API Reference
Base URL
Production: https://api.yebocars.com (planned)
Current: https://bamzu-api-[hash]-uc.a.run.appAuthentication
Most endpoints require Bearer token:
Authorization: Bearer <access_token>Endpoints
Authentication
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /auth/signup | No | Register (sends OTP) |
| POST | /auth/verify-signup | No | Complete registration with OTP |
| POST | /auth/login | No | Login with phone + password |
| POST | /auth/refresh-token | No | Refresh access token |
| POST | /auth/forgot-password | No | Request password reset OTP |
| POST | /auth/verify-forgot-password | No | Verify reset OTP |
| POST | /auth/reset-password | No | Set new password |
| POST | /auth/resend-otp | No | Resend verification code |
| GET | /auth/me | Yes | Get current user |
| POST | /auth/logout | Yes | Logout (client-side) |
| GET | /auth/dealer-application-status | Yes | Check dealer application |
Cars
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /cars | No | List cars (paginated, filtered) |
| GET | /cars/:id | No | Get car by ID |
| POST | /cars | Yes | Create car listing |
| PUT | /cars/:id | Yes | Update car |
| DELETE | /cars/:id | Yes | Delete car |
| GET | /cars/my-cars | Yes | Get user's cars |
| GET | /cars/makes | No | Get available makes |
| GET | /cars/models/:make | No | Get models for make |
| GET | /cars/body-types | No | Get body types |
| GET | /cars/features | No | Get feature list |
| GET | /cars/templates/:make/:model | No | Get car template |
| POST | /cars/advanced-search | No | Advanced search |
| GET | /cars/:id/analytics | Yes | Get car analytics |
| POST | /cars/:id/like | Yes | Like car |
| GET | /cars/:id/views | No | Get view count |
| POST | /cars/:id/views | No | Increment views |
| GET | /cars/:id/comments | No | Get comments |
| POST | /cars/:id/comments | Yes | Post comment |
| POST | /cars/:id/share | No | Generate share link |
| POST | /cars/:id/test-drive | Yes | Schedule test drive |
| GET | /cars/trending | No | Get trending cars |
| POST | /cars/stats/batch | No | Batch car stats |
| POST | /cars/likes/batch-status | Yes | Batch like status |
| GET | /cars/:id/stats | No | Get car stats |
| GET | /cars/:id/likes | No | Get like count |
| GET | /cars/user/liked | Yes | Get user's liked cars |
| POST | /cars/comments/:commentId/like | Yes | Like comment |
| POST | /cars/comments/:commentId/reply | Yes | Reply to comment |
Dealers
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /dealers | No | List dealers |
| GET | /dealers/:id | No | Get dealer by ID |
| GET | /dealers/:id/cars | No | Get dealer's cars |
| PUT | /dealers/:id/approve | Admin | Approve dealer |
| GET | /dealers/admin/pending | Admin | Get pending dealers |
| GET | /dealers/profile/me | Dealer | Get own profile |
| PUT | /dealers/profile/me | Dealer | Update profile |
| GET | /dealers/profile/business-hours | Dealer | Get business hours |
| PUT | /dealers/profile/business-hours | Dealer | Update hours |
| PUT | /dealers/profile/business-info | Dealer | Update business info |
| GET | /dealers/stats | Dealer | Get dashboard stats |
| GET | /dealers/export/cars | Dealer | Export car listings |
| GET | /dealers/:id/reviews | No | Get dealer reviews |
| POST | /dealers/:id/reviews | Yes | Submit review |
Dealer Applications
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /dealer-applications | Yes | Submit application |
| GET | /dealer-applications/my-applications | Yes | Get user's applications |
| PUT | /dealer-applications/:id/approve | Admin | Approve application |
| PUT | /dealer-applications/:id/reject | Admin | Reject application |
Customers
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /customers/favorites | Yes | Get favorites |
| POST | /customers/favorites/:carId | Yes | Add to favorites |
| DELETE | /customers/favorites/:carId | Yes | Remove from favorites |
| POST | /customers/inquiries | Yes | Send inquiry |
| GET | /customers/inquiries | Yes | Get inquiries |
AI
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /ai/generate-listing | Yes | AI-generate description |
| POST | /ai/enhance-description | Yes | Enhance listing |
| POST | /ai/search | No | Natural language search |
| POST | /ai/recommendations | Yes | Lifestyle recommendations |
| GET | /ai/pricing-insights/:carId | Yes | Get pricing insights |
VIN
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /vin/:vin | No | VIN lookup |
Media
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /media/upload | Yes | Upload media |
| DELETE | /media/:id | Yes | Delete media |
Countries
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /countries | No | List countries |
| GET | /countries/:code | No | Get country by code |
| GET | /countries/supported | No | Get supported countries |
Messages
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /messages/conversations | Yes | Get conversations |
| GET | /messages/:receiverId | Yes | Get messages with user |
| POST | /messages | Yes | Send message |
| PUT | /messages/:id/read | Yes | Mark as read |
| GET | /messages/unread-count | Yes | Get unread count |
Notifications
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /notifications/price-alert | Yes | Create price alert |
| GET | /notifications/price-alerts | Yes | Get price alerts |
| DELETE | /notifications/price-alert/:id | Yes | Delete price alert |
| POST | /notifications/new-car-alert | Yes | Create new car alert |
| GET | /notifications/new-car-alerts | Yes | Get new car alerts |
| PUT | /notifications/new-car-alert/:id | Yes | Update alert |
| DELETE | /notifications/new-car-alert/:id | Yes | Delete alert |
Billing
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/billing/plans | No | Get subscription plans |
| POST | /api/billing/checkout | Yes | Create checkout session |
| POST | /api/billing/webhook | No | Stripe webhook |
Blog
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/blog/posts | No | List posts |
| GET | /api/blog/posts/:slug | No | Get post by slug |
| POST | /api/blog/posts | Admin | Create post |
| PUT | /api/blog/posts/:id | Admin | Update post |
| DELETE | /api/blog/posts/:id | Admin | Delete post |
Dashboard
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/dashboard/metrics | API Key | Get CEO metrics |
Service Architecture
System Components
┌─────────────────────────────────────────────────────────────┐
│ YeboCars System │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ React App │ │ Express │ │ PostgreSQL │ │
│ │ (Frontend) │ │ API │ │ (Neon) │ │
│ │ │ │ │ │ │ │
│ │ Vite + TS │ │ Node.js + │ │ Via Prisma │ │
│ │ TailwindCSS │ │ TypeScript │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └─────────────────┼─────────────────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Stripe │ │ YeboLink │ │ Google AI │ │
│ │ Payments │ │ (OTP/SMS) │ │ (Gemini) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Tech Stack
| Component | Technology |
|---|---|
| Frontend | React 18 + TypeScript + Vite |
| Styling | TailwindCSS |
| Icons | Lucide React |
| API | Express.js + TypeScript |
| Database | PostgreSQL (Neon) |
| ORM | Prisma 7.4 |
| Auth | JWT (access + refresh tokens) |
| Passwords | bcrypt (cost 12) |
| Validation | Joi |
| Payments | Stripe |
| OTP/SMS | YeboLink (WhatsApp templates) |
| AI | Google Gemini |
| File Upload | Multer + S3 |
| Hosting | Cloud Run |
| CDN/Proxy | Cloudflare |
Service Layers
┌─────────────────────────────────────────────────────────────┐
│ ROUTES │
│ auth.routes.ts, car.routes.ts, dealer.routes.ts, etc. │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CONTROLLERS │
│ auth.controller.ts, car.controller.ts, etc. │
│ - Request validation (Joi schemas) │
│ - Route handling │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SERVICES │
│ auth.service.ts, car.service.ts, ai.service.ts, etc. │
│ - Business logic │
│ - Data transformation │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PRISMA CLIENT │
│ - Database operations │
│ - Query building │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ POSTGRESQL │
│ Neon Serverless │
└─────────────────────────────────────────────────────────────┘Middleware Stack
- Helmet — Security headers
- Compression — Response compression
- CORS — Cross-origin handling
- Rate Limiter — 100 requests/15min default
- Body Parser — JSON (10MB limit) + raw for Stripe
- Authentication — JWT validation
- Country Context — Auto-detect country
- Validation — Joi schema validation
- Error Handler — Centralized error handling
Authentication
Phone + Password Flow
Signup:
- User provides phone + password
- OTP sent via YeboLink (WhatsApp template)
- User verifies OTP → account created
- Returns JWT tokens
Login:
- User provides phone + password
- Password verified via bcrypt
- Returns JWT tokens
Token Architecture:
- Access Token: 15 minutes (configurable)
- Refresh Token: 30 days
- Stored in localStorage (client)
Access Token Payload
{
"userId": "cuid123",
"whatsapp": "+26878422613",
"role": "user",
"iat": 1710770000,
"exp": 1710770900
}OTP Flow
User requests OTP
│
▼
Server generates 6-digit code
│
▼
Code stored in Otp table (10 min expiry)
│
▼
YeboLink sends WhatsApp template message
│
▼
User enters OTP
│
▼
Server validates, marks as used
│
▼
Action completed (signup/reset)Billing & Monetization
Subscription Plans
| Plan | Price (USD) | Listing Limit | Features |
|---|---|---|---|
| FREE | $0 | 3 | Basic listing |
| BASIC | $10/month | 10 | Basic analytics |
| DEALER | $25/month | 50 | Lead management, featured slots |
| PRO | $60/month | Unlimited | Priority placement, advanced analytics |
Currency Localization
- Display: User sees price in local currency (ZAR, NGN, KES, etc.)
- Charge: Stripe charges in supported currency or falls back to USD
- Conversion: Server-side FX rates (approximate, manually updated)
Supported African Currencies
| Country | Code | Stripe Supported |
|---|---|---|
| South Africa | ZAR | ✓ |
| Nigeria | NGN | ✓ |
| Kenya | KES | ✓ |
| Ghana | GHS | ✓ |
| Egypt | EGP | ✓ |
| Eswatini | SZL | ✗ (USD fallback) |
| Tanzania | TZS | ✗ (USD fallback) |
| Uganda | UGX | ✗ (USD fallback) |
| Rwanda | RWF | ✗ (USD fallback) |
| ... | ... | ... |
Stripe Integration
- Mode: Supports test and live modes via
STRIPE_MODEenv - Webhooks:
checkout.session.completedupdates dealer plan - Customer ID: Stored for future payments
Technical Stack
Backend Dependencies
{
"@prisma/client": "^7.4.0",
"express": "^4.18.2",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"joi": "^17.11.0",
"stripe": "^20.3.1",
"@google/generative-ai": "^0.24.1",
"mongoose": "^9.2.1",
"socket.io": "^4.7.2",
"multer": "^1.4.5-lts.1",
"@aws-sdk/client-s3": "^3.872.0"
}Frontend Dependencies
{
"react": "^18.3.1",
"react-dom": "^18.3.1",
"lucide-react": "^0.344.0",
"vite": "^5.4.2",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3"
}Environment Variables
Backend:
DATABASE_URL— PostgreSQL connection stringJWT_ACCESS_SECRET— Access token secretJWT_REFRESH_SECRET— Refresh token secretSTRIPE_SECRET_KEY— Stripe API keySTRIPE_WEBHOOK_SECRET— Stripe webhook signing secretSTRIPE_MODE— test or liveGOOGLE_API_KEY— Gemini API keyYEBOLINK_API_URL— YeboLink endpoint for OTPsALLOWED_ORIGINS— CORS whitelist
Gaps & Missing Features
Critical Gaps
YeboID Integration
- Currently has standalone auth
- Needs migration to YeboID for unified Yebo identity
- Phone + PIN auth vs current phone + password
YeboSafe Integration
- No escrow system
- No secure payment for P2P transactions
- Dealers handle payments externally
Media Processing
- Schema includes
mediaProcessingStatusbut not fully implemented - Image optimization pipeline incomplete
- No video transcoding
- Schema includes
Real-time Features
- WebSocket service exists but underutilized
- Chat could use real-time updates
- No live typing indicators
Mobile Apps
- Web-only currently
- No React Native app
- PWA not configured
Feature Gaps
Advanced Search
- Vector search for car models seeded but not production-ready
- Voyage AI service referenced but not active
- Embedding generation incomplete
Notifications
- Price/new car alerts exist but background jobs need scheduling
- No push notifications (web/mobile)
- No email notifications
Analytics
- Basic view tracking
- No conversion tracking
- No A/B testing infrastructure
Localization
- Multi-currency display works
- No multi-language support
- Content all in English
Admin Dashboard
- Basic approval flows
- No comprehensive admin panel
- No moderation tools
Verification
- No YeboVerify integration
- No seller verification badges
- Trust system incomplete
Technical Debt
Mixed ORM Usage
- Some code references Mongoose (legacy)
- Should be fully Prisma
@ts-nocheckin multiple services
Error Handling
- Inconsistent error responses
- Some services throw generic errors
- Need standardized error codes
Testing
- Jest configured but few tests
- No integration tests
- No E2E tests
Documentation
- Swagger docs exist but incomplete
- No API versioning implemented
- README outdated
Roadmap
Phase 1: Stabilization (Q1 2026)
- [x] Prisma migration from Mongoose
- [x] Multi-country support
- [x] Stripe billing integration
- [ ] Complete ORM migration (remove Mongoose refs)
- [ ] Add comprehensive tests
- [ ] Production deployment (yebocars.com)
Phase 2: YeboID Integration (Q2 2026)
- [ ] Migrate to YeboID authentication
- [ ] Phone + PIN auth
- [ ] User consolidation from Bamzu
- [ ] Unified profile with Yebo ecosystem
Phase 3: Trust & Safety (Q2-Q3 2026)
- [ ] YeboVerify integration
- [ ] Seller verification badges
- [ ] Fraud detection
- [ ] Reporting system
Phase 4: YeboSafe Integration (Q3 2026)
- [ ] Escrow for P2P transactions
- [ ] Mobile money integration
- [ ] Secure checkout flow
Phase 5: Mobile & Scale (Q4 2026)
- [ ] React Native mobile app
- [ ] PWA configuration
- [ ] Push notifications
- [ ] Performance optimization
Phase 6: AI Enhancement (2027)
- [ ] Production vector search
- [ ] AI car valuations
- [ ] Personalized recommendations
- [ ] Chatbot assistant
Appendix
Database Schema Visualization
┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │────▶│ Car │◀────│ Dealer │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Favorite │ │ CarLike │ │ Inquiry │
│ CarView │ │CarComment│ │TestDrive │
│ Message │ │ CarShare │ │ Review │
│PriceAlert│ │Analytics │ │Application│
└──────────┘ └──────────┘ └──────────┘
│
│
▼
┌──────────┐
│ Country │
│ CarMake │
│ CarModel │
│ Otp │
│ BlogPost │
└──────────┘Example API Response
GET /cars
{
"success": true,
"data": [
{
"_id": "clm1234567890",
"make": "Toyota",
"model": "Camry",
"year": 2022,
"price": 450000,
"mileage": 25000,
"fuelType": "Petrol",
"transmission": "Automatic",
"bodyType": "Sedan",
"color": "White",
"description": "Well-maintained...",
"images": ["https://..."],
"features": ["Air Conditioning", "Bluetooth"],
"sellerId": "clm0987654321",
"sellerType": "dealer",
"sellerName": "AutoWorld",
"condition": "Used",
"status": "active",
"currency": { "code": "SZL", "symbol": "L", "name": "Swazi Lilangeni" },
"formattedPrice": "L450,000",
"countryId": "clm_sz_country",
"createdAt": "2026-02-20T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 156,
"pages": 16
}
}Document Version: 1.0
Last Updated: March 19, 2026
Author: Yebo Product Team