Architecture, Workflows, ER Diagrams & Complete Technical Reference for the Chef Booking Platform
Every technology powering Sizzle, from frontend to deployment
Modern single-page app with progressive web app capabilities, server state caching, and smooth animations.
Native mobile app from the same React codebase. GPS geolocation, splash screen, status bar integration.
High-performance async API server with Pydantic validation, SQLAlchemy ORM, and automatic OpenAPI docs.
Relational database for all entities. Redis for OTP storage, rate limiting, and session caching.
Passwordless authentication via email OTP. Three-tier email delivery with automatic fallback.
Indian UPI payments via PhonePe gateway. Dockerized deployment on GCP Cloud Run with Nginx.
How all components connect — from user devices to databases and external services
graph TB
subgraph Clients["📱 Client Layer"]
direction LR
WEB["🌐 Web Browser\n(React SPA / PWA)"]
ANDROID["📱 Android App\n(Capacitor 6)"]
end
subgraph Gateway["🔀 Reverse Proxy"]
NGINX["Nginx\nStatic Files · API Proxy\nGzip · Security Headers"]
end
subgraph Backend["⚙️ Application Server"]
FASTAPI["FastAPI + Uvicorn\nASGI · Port 8000"]
subgraph MW["Middleware Pipeline"]
CORS["CORS\nOrigin Filter"]
JWT_MW["JWT Auth\nToken Validation"]
ROLE["Role Guard\n🧑Customer · 👨🍳Chef · 🛡️Admin"]
end
subgraph BL["Business Logic Services"]
AUTH_S["🔐 Auth Service\nOTP · JWT · Email"]
BOOK_S["📋 Booking Service\nPricing · CRUD"]
PAY_S["💳 Payment Service\nPhonePe UPI"]
NOTIF_S["🔔 Notification Service\nEmail · Push"]
end
end
subgraph Data["💾 Data Layer"]
PG[("🐘 PostgreSQL 16\n8 Tables · UUID PKs\nAsync via AsyncPG")]
REDIS[("🔴 Redis 7\nOTP Store · Rate Limiter\n5 min TTL")]
end
subgraph External["🌍 External Services"]
RESEND["📧 Resend API\n3K emails/mo free"]
GMAIL["📧 Gmail SMTP\nFallback email"]
PHONEPE["💳 PhonePe\nUPI Payments"]
OSM["🗺️ OpenStreetMap\nMaps + Geocoding"]
end
WEB & ANDROID --> NGINX
NGINX -->|"/api/v1/*"| FASTAPI
NGINX -->|"/*"| WEB
FASTAPI --> CORS --> JWT_MW --> ROLE
ROLE --> AUTH_S & BOOK_S & PAY_S & NOTIF_S
AUTH_S --> PG & REDIS & RESEND & GMAIL
BOOK_S --> PG
PAY_S --> PG & PHONEPE
NOTIF_S --> RESEND
WEB & ANDROID -.->|"Map tiles"| OSM
style Clients fill:#FFF7ED,stroke:#EA580C,stroke-width:2px
style Gateway fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style Backend fill:#F0FDF4,stroke:#16A34A,stroke-width:2px
style Data fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style External fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
Complete database schema — 8 tables, all fields, types, and relationships
erDiagram
USERS {
uuid id PK
string name "nullable"
string email UK "indexed"
string phone UK "indexed"
enum role "customer | chef | admin"
date dob "nullable"
boolean is_active "default true"
string avatar_url "nullable"
timestamp created_at
timestamp updated_at
}
CHEF_PROFILES {
uuid id PK
uuid user_id FK, UK "unique → users"
text bio "nullable"
array specialties "string[]"
int experience_years "default 0"
float rating "nullable"
int total_bookings "default 0"
float hourly_rate "default 399.0 INR"
boolean is_available "default true"
boolean is_verified "default false"
string service_city "nullable"
timestamp created_at
timestamp updated_at
}
CHEF_AVAILABILITY {
uuid id PK
uuid chef_profile_id FK "→ chef_profiles"
enum day_of_week "mon-sun"
time start_time "HH:MM"
time end_time "HH:MM"
boolean is_available "default true"
}
ADDRESSES {
uuid id PK
uuid user_id FK "→ users, indexed"
string label "Home | Office | Other"
text full_address "required"
string city "required"
string state "nullable"
string pincode "required"
float latitude "GPS"
float longitude "GPS"
boolean is_default "default false"
timestamp created_at
}
BOOKINGS {
uuid id PK
uuid customer_id FK "→ users, indexed"
uuid chef_id FK "→ users, nullable"
uuid address_id FK "→ addresses"
date booking_date "required"
time time_slot "HH:MM"
int duration_hours "1-8, default 2"
int guest_count "1-20, default 2"
text special_requests "nullable"
decimal total_amount "10,2 INR"
enum status "pending|confirmed|in_progress|completed|cancelled"
text cancellation_reason "nullable"
string booking_ref UK "SZ-XXXXXXXX"
timestamp created_at
timestamp updated_at
}
PAYMENTS {
uuid id PK
uuid booking_id FK, UK "→ bookings"
decimal amount "10,2 INR"
enum status "pending|success|failed|refunded"
string transaction_id UK "PhonePe TXN"
string merchant_transaction_id UK "SZ_uuid"
string payment_method "UPI"
jsonb gateway_response "raw PhonePe"
timestamp created_at
timestamp updated_at
}
LOCATIONS {
uuid id PK
string city "indexed"
string state "required"
string pincode "nullable"
boolean is_serviceable "default true"
timestamp created_at
}
SERVICE_AREAS {
uuid id PK
uuid location_id FK "→ locations, CASCADE"
string name "area name"
string city "indexed"
float latitude "center"
float longitude "center"
float radius_km "default 10.0"
boolean is_active "default true"
timestamp created_at
}
USERS ||--o| CHEF_PROFILES : "has profile (if chef)"
USERS ||--o{ ADDRESSES : "has many"
USERS ||--o{ BOOKINGS : "creates (as customer)"
USERS ||--o{ BOOKINGS : "fulfills (as chef)"
CHEF_PROFILES ||--o{ CHEF_AVAILABILITY : "has weekly slots"
BOOKINGS ||--o| PAYMENTS : "has payment"
BOOKINGS }o--|| ADDRESSES : "service location"
LOCATIONS ||--o{ SERVICE_AREAS : "has areas"
Three distinct user journeys — Customer, Chef, and Admin
flowchart LR
A["📱 Open App"] --> B["✉️ Login\n(Email OTP)"]
B --> C{"🆕 New\nUser?"}
C -->|Yes| D["📝 Onboarding\n(Name · Phone)"]
C -->|No| E["🏠 Dashboard"]
D --> E
E --> F["📍 Select or\nAdd Address"]
F --> G["📅 Pick Date\n& Time Slot"]
G --> H["⏱️ Set Duration\n& Guest Count"]
H --> I["📋 Review\nPrice Breakdown"]
I --> J["💳 Pay via\nPhonePe UPI"]
J --> K["✅ Booking\nConfirmed!"]
K --> L["🔔 Track\nLive Status"]
style A fill:#FFF7ED,stroke:#EA580C,stroke-width:2px
style E fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style K fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
style J fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
flowchart LR
A["🔑 Login"] --> B["📝 Profile Setup\nBio · Specialties · Rate"]
B --> C["📅 Set Weekly\nAvailability"]
C --> D["📥 Receive\nBooking"]
D --> E{"✅ Accept?"}
E -->|Yes| F["👍 Confirm\nBooking"]
E -->|No| G["❌ Decline"]
F --> H["🍳 Start\nCooking"]
H --> I["✅ Mark\nCompleted"]
I --> J["💰 Earnings\nUpdated"]
style D fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style I fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
style J fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
flowchart TB
A["🛡️ Admin Dashboard"] --> B["📊 View Stats\nUsers · Revenue · Bookings"]
A --> C["👨🍳 Manage Chefs"]
A --> D["📋 Manage Bookings"]
A --> E["📍 Manage Locations"]
C --> C1["➕ Register New Chef"]
C --> C2["✅ Verify Chef"]
C --> C3["📅 Set Chef Availability"]
C --> C4["✏️ Edit Chef Profile"]
D --> D1["👁️ View All Bookings"]
D --> D2["👨🍳 Assign Chef"]
D --> D3["🔄 Update Status"]
E --> E1["🏙️ Add City"]
E --> E2["📍 Add Service Area"]
E --> E3["🟢 Toggle Serviceability"]
style A fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
Passwordless OTP authentication with JWT tokens and auto-refresh
sequenceDiagram
autonumber
participant U as 📱 User
participant F as 🌐 Frontend
participant B as ⚙️ Backend
participant R as 🔴 Redis
participant E as 📧 Email Service
U->>F: Enter email address
F->>B: POST /auth/send-otp {email}
B->>R: Check rate limit (max 5/5min)
R-->>B: ✅ Under limit
B->>B: Generate 6-digit OTP
B->>R: Store OTP (TTL: 5 min)
alt 🟢 Resend API available
B->>E: Send via Resend API
else 🟡 SMTP fallback
B->>E: Send via Gmail SMTP
else 🔵 Dev mode
B->>B: Log OTP to console
end
E-->>U: 📩 Branded HTML email with OTP
B-->>F: {message: "OTP sent"}
U->>F: Enter 6-digit OTP
F->>B: POST /auth/verify-otp {email, otp}
B->>R: Verify OTP & delete
R-->>B: ✅ Match
B->>B: Get or create user
B->>B: Generate access + refresh JWT
B-->>F: {access_token, refresh_token, role, is_new_user}
F->>F: Save tokens → localStorage
alt 🆕 New user
F->>U: Redirect → /onboarding
else 🧑 Customer
F->>U: Redirect → /customer/dashboard
else 👨🍳 Chef
F->>U: Redirect → /chef/profile
else 🛡️ Admin
F->>U: Redirect → /admin/dashboard
end
sequenceDiagram
autonumber
participant F as 🌐 Frontend (Axios)
participant B as ⚙️ Backend
F->>B: API request (expired access_token)
B-->>F: 401 Unauthorized
F->>F: Get refresh_token from localStorage
F->>B: POST /auth/refresh {refresh_token}
alt ✅ Valid refresh token
B-->>F: {new access_token, new refresh_token}
F->>F: Update both tokens in localStorage
F->>B: 🔄 Retry original request (new token)
B-->>F: ✅ Success response
else ❌ Invalid/expired refresh token
B-->>F: 401 Invalid token
F->>F: Clear all tokens
F->>F: Redirect → /login
end
State machine and end-to-end booking creation sequence
stateDiagram-v2
[*] --> PENDING: Customer creates booking
PENDING --> CONFIRMED: Chef/Admin confirms
PENDING --> CANCELLED: Customer cancels
CONFIRMED --> IN_PROGRESS: Chef starts cooking
CONFIRMED --> CANCELLED: Either party cancels
IN_PROGRESS --> COMPLETED: Chef finishes service
COMPLETED --> [*]
CANCELLED --> [*]
note right of PENDING
🏷️ Ref: SZ-XXXXXXXX
💳 Payment initiated
⏳ Awaiting chef
end note
note right of CONFIRMED
👨🍳 Chef assigned
💰 Payment successful
📱 Notifications sent
end note
note right of IN_PROGRESS
🍳 Chef on-site
🕐 Timer running
📍 At customer address
end note
note right of COMPLETED
✅ Service delivered
⭐ Rating eligible
💰 Chef paid
end note
note right of CANCELLED
📝 Reason recorded
💸 Refund eligible
end note
sequenceDiagram
autonumber
participant C as 👤 Customer
participant F as 🌐 Frontend
participant B as ⚙️ Backend
participant DB as 🐘 PostgreSQL
C->>F: Tap address from saved list
F->>F: Auto-advance → datetime step
C->>F: Pick date + time slot (e.g. 19:00)
F->>F: Advance → duration step
C->>F: Set duration (3 hrs) + guests (8)
F->>F: Calculate price breakdown (client-side mirror)
Note over F: Base: ₹399×3 = ₹1,197
Note over F: Surcharge: (8-4)×₹49×3 = ₹588
Note over F: Peak 20%: ₹2,142
Note over F: Platform 5%: ₹107.10
Note over F: GST 5%: ₹112.46
Note over F: Total: ₹2,361.56
C->>F: Review & Confirm
F->>B: POST /customer/bookings
B->>DB: Fetch chef hourly_rate (₹399)
B->>B: Server-side price calculation
B->>B: Generate ref: SZ-A1B2C3D4
B->>DB: INSERT INTO bookings
DB-->>B: Booking created
B-->>F: {id, ref: "SZ-A1B2C3D4", total: 2361.56, status: "pending"}
F-->>C: ✅ "Booking Created!" with breakdown
Smart Indian market pricing — fair for customers, profitable for chefs
flowchart TD
START(["📋 Booking Request\nrate · hours · guests · time"]):::start --> BASE["💵 Base Cost\nchef_rate × duration_hours\n(default ₹399/hr)"]
BASE --> GUEST_CHECK{"👥 Guests > 4?"}
GUEST_CHECK -->|"Yes"| SURCHARGE["💵 Guest Surcharge\n(guests - 4) × ₹49 × hours"]
GUEST_CHECK -->|"No (≤ 4)"| NO_SURCHARGE["✅ No surcharge\n₹0"]
SURCHARGE --> PEAK_CHECK
NO_SURCHARGE --> PEAK_CHECK
PEAK_CHECK{"🌙 Time between\n18:00 - 21:00?"}
PEAK_CHECK -->|"Yes (Dinner)"| PEAK["🔥 Peak Premium\nsubtotal × 1.20\n(+20%)"]
PEAK_CHECK -->|"No"| NO_PEAK["✅ No premium\nsubtotal × 1.00"]
PEAK --> PLATFORM
NO_PEAK --> PLATFORM
PLATFORM["🏢 Platform Fee\n5% of subtotal"]
PLATFORM --> GST["🏛️ GST\n5% of (subtotal + platform_fee)"]
GST --> TOTAL(["💰 TOTAL\nsubtotal + platform_fee + GST\n(rounded to ₹X.XX)"]):::total
classDef start fill:#FFF7ED,stroke:#EA580C,stroke-width:2px
classDef total fill:#DCFCE7,stroke:#16A34A,stroke-width:3px
| Scenario | Rate/hr | Hours | Guests | Time | Base | Surcharge | Peak | Platform | GST | Total |
|---|---|---|---|---|---|---|---|---|---|---|
| Basic lunch | ₹399 | 2 | 4 | 12:00 | ₹798 | ₹0 | ₹798 | ₹39.90 | ₹41.90 | ₹879.80 |
| Dinner party | ₹399 | 3 | 8 | 19:00 | ₹1,197 | ₹588 | ₹2,142 | ₹107.10 | ₹112.46 | ₹2,361.56 |
| Small dinner | ₹399 | 2 | 2 | 20:00 | ₹798 | ₹0 | ₹957.60 | ₹47.88 | ₹50.27 | ₹1,055.75 |
| Big gathering | ₹599 | 4 | 15 | 13:00 | ₹2,396 | ₹2,156 | ₹4,552 | ₹227.60 | ₹238.98 | ₹5,018.58 |
PhonePe UPI integration — initiation, redirect, webhook, and verification
sequenceDiagram
autonumber
participant C as 👤 Customer
participant F as 🌐 Frontend
participant B as ⚙️ Backend
participant PP as 💳 PhonePe
participant DB as 🐘 PostgreSQL
C->>F: Tap "Pay Now"
F->>B: POST /payments/initiate {booking_id}
B->>DB: Get booking (amount, ref)
B->>B: Generate merchant_txn_id (SZ_uuid)
B->>B: Create base64 payload
B->>B: Calculate X-VERIFY checksum (SHA256 + salt)
B->>PP: POST /pg/v1/pay (payload, X-VERIFY)
PP-->>B: {success: true, payment_url}
B->>DB: INSERT payment (status: pending)
B-->>F: {payment_url, merchant_txn_id}
F->>C: 🔗 Redirect to PhonePe
C->>PP: Complete UPI payment (PIN)
PP-->>C: Payment result page
par Webhook (server-to-server)
PP->>B: POST /payments/callback (signed)
B->>B: Verify X-VERIFY checksum
B->>DB: UPDATE payment status → success
B->>DB: UPDATE booking status → confirmed
and Redirect (user-facing)
PP->>F: Redirect → /booking/status?ref=SZ-XXX
F->>B: GET /payments/status/{txn_id}
B-->>F: {status: "success", amount: 2361.56}
F-->>C: ✅ "Payment Successful!"
end
28 RESTful endpoints organized by role and domain
graph LR
API["🔗 /api/v1"] --> AUTH["🔓 /auth\n(Public)"]
API --> CUST["🧑 /customer\n(JWT + Customer)"]
API --> CHEF["👨🍳 /chef\n(JWT + Chef)"]
API --> ADMIN["🛡️ /admin\n(JWT + Admin)"]
API --> PAY["💳 /payments\n(JWT + Webhook)"]
AUTH --> A1["POST send-otp"]
AUTH --> A2["POST verify-otp"]
AUTH --> A3["POST refresh"]
CUST --> C1["GET|PUT profile"]
CUST --> C2["CRUD addresses (4)"]
CUST --> C3["POST check-serviceability"]
CUST --> C4["POST|GET bookings (3)"]
CHEF --> CH1["GET|PUT profile"]
CHEF --> CH2["POST|DEL availability"]
CHEF --> CH3["GET bookings + PUT status"]
ADMIN --> AD1["GET stats"]
ADMIN --> AD2["CRUD chefs (6)"]
ADMIN --> AD3["GET|PUT bookings"]
ADMIN --> AD4["CRUD locations (4)"]
ADMIN --> AD5["POST|DEL service-areas"]
PAY --> P1["POST initiate"]
PAY --> P2["POST callback"]
PAY --> P3["GET status"]
style AUTH fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
style CUST fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style CHEF fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style ADMIN fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
style PAY fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
React component tree, state management, and routing
graph TB
subgraph App["📱 React Application"]
ROUTER["🔀 React Router v6\nRole-Based Routing"]
subgraph AuthPages["🔑 Auth Pages"]
LOGIN["LoginPage\nEmail → OTP → JWT"]
ONBOARD["OnboardingPage\nName · Phone · Role"]
end
subgraph CustPages["🧑 Customer Pages"]
CDASH["DashboardPage\nOverview · Quick Book"]
CBOOK["BookingsListPage\nAll Bookings"]
CNEW["NewBookingPage\n5-Step Wizard"]
CDET["BookingDetailPage\nView · Cancel · Pay"]
CPROF["ProfilePage\nEdit · Addresses"]
end
subgraph ChefPages["👨🍳 Chef Pages"]
CHPROF["ChefProfilePage\nBio · Rate · Slots"]
CHBOOK["ChefBookingsPage\nManage · Status"]
end
subgraph AdminPages["🛡️ Admin Pages"]
ADASH["AdminDashboardPage\nStats · Analytics"]
ACHEF["AdminChefsPage\nCRUD · Verify"]
ABOOK["AdminBookingsPage\nAll · Assign"]
ALOC["AdminLocationsPage\nCities · Areas"]
end
end
subgraph State["📦 State Layer"]
ZUSTAND["🐻 Zustand Store\nauth · user · tokens\nlocalStorage persist"]
RQ["🔄 React Query\nServer state · Auto-refetch\nCaching · Mutations"]
end
subgraph UILib["🎨 UI Layer"]
TW["Tailwind CSS 3.4"]
FM["Framer Motion 11"]
TOAST["React Hot Toast"]
LEAFLET["React Leaflet"]
ICONS["React Icons"]
end
subgraph APILayer["🌐 API Layer"]
AXIOS["Axios Instance\nBase URL · Bearer Auth\n401 Auto-Refresh"]
end
subgraph Mobile["📱 Capacitor 6"]
GEO["GPS Geolocation"]
SPLASH["Splash Screen"]
SBAR["Status Bar"]
end
ROUTER --> AuthPages & CustPages & ChefPages & AdminPages
CustPages & ChefPages & AdminPages --> State
State --> APILayer
CustPages & ChefPages & AdminPages --> UILib
App --> Mobile
style App fill:#FFF7ED,stroke:#EA580C,stroke-width:2px
style State fill:#DBEAFE,stroke:#2563EB
style UILib fill:#FEF3C7,stroke:#D97706
style APILayer fill:#DCFCE7,stroke:#16A34A
style Mobile fill:#F5F3FF,stroke:#7C3AED
flowchart TD
ROOT["/ (Root)"] --> LOGIN["/login"]
ROOT --> ONBOARD["/onboarding"]
ROOT --> CUST["/customer"]
CUST --> CDASH["/customer/dashboard\n🏠 Home Overview"]
CUST --> CBOOK["/customer/bookings\n📋 Booking History"]
CUST --> CNEW["/customer/bookings/new\n✨ Create Booking"]
CUST --> CDET["/customer/bookings/:id\n👁️ Booking Details"]
CUST --> CPROF["/customer/profile\n⚙️ Settings"]
ROOT --> CHEF["/chef"]
CHEF --> CHPROF["/chef/profile\n📝 Edit Profile"]
CHEF --> CHBOOK["/chef/bookings\n📋 My Bookings"]
ROOT --> ADMIN["/admin"]
ADMIN --> ADASH["/admin/dashboard\n📊 Analytics"]
ADMIN --> ACHEF["/admin/chefs\n👨🍳 Chef Mgmt"]
ADMIN --> ABOOK["/admin/bookings\n📋 All Bookings"]
ADMIN --> ALOC["/admin/locations\n📍 Locations"]
style ROOT fill:#0F172A,color:#FFF
style LOGIN fill:#FFF7ED,stroke:#EA580C
style CUST fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style CHEF fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style ADMIN fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
Three deployment modes — local dev, Docker production, and GCP Cloud Run
graph LR
subgraph Local["🖥️ Local Machine"]
VITE["⚡ Vite Dev Server\nlocalhost:5173\nHot Module Reload"]
UVICORN["🐍 Uvicorn\nlocalhost:8000\n--reload mode"]
subgraph Docker["🐳 Docker Compose"]
PG["🐘 PostgreSQL 16\nlocalhost:5432"]
RD["🔴 Redis 7\nlocalhost:6379"]
end
end
VITE -->|"VITE_API_URL"| UVICORN
UVICORN --> PG & RD
style Local fill:#F0FDF4,stroke:#16A34A,stroke-width:2px
style Docker fill:#DBEAFE,stroke:#2563EB
graph TB
INTERNET["🌐 Internet\nUsers"] --> NGINX
subgraph DockerProd["🐳 Docker Compose Production"]
NGINX["📦 Nginx Container\nPort 80 / 443\nStatic + Reverse Proxy"]
BACKEND["📦 Backend Container\nFastAPI + Uvicorn\nPort 8000"]
PG["📦 PostgreSQL 16\nPort 5432\nPersistent Volume"]
REDIS["📦 Redis 7\nPort 6379\nLRU · 256MB"]
end
NGINX -->|"/ → static files"| NGINX
NGINX -->|"/api/* → proxy"| BACKEND
BACKEND --> PG & REDIS
style DockerProd fill:#FFF7ED,stroke:#EA580C,stroke-width:2px
graph TB
USERS["👥 Users\n(Web + Android)"] --> CR
subgraph GCP["☁️ Google Cloud Platform"]
CR["🚀 Cloud Run\nasia-south1 (Mumbai)\nAuto-scale 0→3\nPort 8080"]
subgraph Container["📦 Single Container"]
SUP["🔧 Supervisord\nProcess Manager"]
NGX["🌐 Nginx\nStatic + Proxy\nPort 8080"]
UVI["🐍 Uvicorn\nFastAPI Backend\nPort 8000"]
end
end
subgraph Free["🆓 Free Tier Services"]
NEON["🐘 Neon.tech\nPostgreSQL\n0.5 GB free"]
UPSTASH["🔴 Upstash\nRedis\n10K cmd/day free"]
end
CR --> Container
SUP --> NGX & UVI
NGX -->|"/api/*"| UVI
UVI --> NEON & UPSTASH
style GCP fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style Container fill:#EFF6FF,stroke:#93C5FD
style Free fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
Complete endpoint reference — 28 endpoints across 5 routers
/api/v1/auth (Public)| Method | Endpoint | Description | Body |
|---|---|---|---|
| POST | /auth/send-otp | Send OTP to email or phone | {email} or {phone} |
| POST | /auth/verify-otp | Verify OTP, return JWT tokens | {email, otp} |
| POST | /auth/refresh | Refresh access token | {refresh_token} |
/api/v1/customer (JWT + Customer role)| Method | Endpoint | Description |
|---|---|---|
| GET | /customer/profile | Get customer profile |
| PUT | /customer/profile | Update customer profile |
| GET | /customer/addresses | List all saved addresses |
| POST | /customer/addresses | Add a new address |
| PUT | /customer/addresses/:id | Update existing address |
| DEL | /customer/addresses/:id | Delete an address |
| POST | /customer/check-serviceability | Check if city is serviceable |
| POST | /customer/bookings | Create a new booking |
| GET | /customer/bookings | List customer bookings (paginated) |
| GET | /customer/bookings/:id | Get booking details |
/api/v1/chef (JWT + Chef role)| Method | Endpoint | Description |
|---|---|---|
| GET | /chef/profile | Get chef profile with availability slots |
| PUT | /chef/profile | Update chef profile |
| POST | /chef/availability | Add availability slot |
| DEL | /chef/availability/:id | Remove availability slot |
| GET | /chef/bookings | List chef's bookings |
| GET | /chef/bookings/:id | Get booking details |
| PUT | /chef/bookings/:id/status | Update booking status |
/api/v1/admin (JWT + Admin role)| Method | Endpoint | Description |
|---|---|---|
| GET | /admin/stats | Dashboard statistics (users, revenue, bookings) |
| GET | /admin/chefs | List all chefs with profiles |
| POST | /admin/chefs | Register a new chef |
| GET | /admin/chefs/:id | Get chef details |
| PUT | /admin/chefs/:id | Update chef profile |
| DEL | /admin/chefs/:id | Deactivate chef |
| POST | /admin/chefs/:id/availability | Bulk set chef availability |
| GET | /admin/bookings | List all bookings (filterable) |
| PUT | /admin/bookings/:id | Update/assign booking |
| GET | /admin/locations | List all locations |
| POST | /admin/locations | Add location/city |
| PUT | /admin/locations/:id | Update location |
| DEL | /admin/locations/:id | Delete location |
| POST | /admin/locations/:id/areas | Add service area |
| DEL | /admin/service-areas/:id | Delete service area |
/api/v1/payments (JWT + Webhook)| Method | Endpoint | Description |
|---|---|---|
| POST | /payments/initiate | Initiate PhonePe UPI payment |
| POST | /payments/callback | PhonePe webhook callback |
| GET | /payments/status/:txn_id | Check payment status |
Multi-layer security from client to database
graph TB
subgraph Client["🌐 Client Security"]
TOKEN["🔑 JWT stored in localStorage\nAccess (7d) + Refresh (30d)"]
HTTPS["🔒 HTTPS only\n(in production)"]
end
subgraph Network["🌐 Network Security"]
CORS_S["🚫 CORS\nRestricted origins only"]
HEADERS["🛡️ Security Headers\nX-Frame-Options · CSP\nX-Content-Type"]
GZIP["📦 Gzip compression"]
end
subgraph Auth["🔐 Authentication"]
OTP_S["📧 Email OTP\n6-digit · 5 min expiry"]
RATE["⏱️ Rate Limiting\nMax 5 OTPs / 5 min (Redis)"]
JWT_S["🎫 JWT HS256\nRole-based claims\nExpiry validation"]
end
subgraph Access["🛡️ Authorization"]
RBAC_S["👥 Role-Based Access\nCustomer · Chef · Admin\nMiddleware guards"]
OWNER["🔒 Ownership Check\nUsers access own data only"]
end
subgraph Data_S["💾 Data Security"]
ORM["🐍 SQLAlchemy ORM\nParameterized queries\nNo raw SQL"]
VALID["✅ Pydantic Validation\nInput sanitization\nType enforcement"]
UUID_S["🔑 UUID Primary Keys\nNon-sequential · Unpredictable"]
end
Client --> Network --> Auth --> Access --> Data_S
style Client fill:#FEE2E2,stroke:#DC2626
style Network fill:#FEF3C7,stroke:#D97706
style Auth fill:#F5F3FF,stroke:#7C3AED
style Access fill:#DBEAFE,stroke:#2563EB
style Data_S fill:#DCFCE7,stroke:#16A34A
Passwordless email OTP, 6-digit, 5 min expiry, rate-limited to 5 attempts per 5 minutes via Redis
HS256 signing, access token (7 days), refresh token (30 days), role claim in payload
Three roles: customer, chef, admin. Middleware guards on every protected route
SQLAlchemy ORM with parameterized queries — no raw SQL anywhere in the codebase
Pydantic v2 schemas validate all request bodies with type enforcement and constraints
Configured allowed origins, only permitted frontend domains can make API requests
Complete directory layout of the Sizzle monorepo