This is a submission for the DEV's Worldwide Show and Tell Challenge Presented by Mux
What I Built
AyurAI is a comprehensive AI-powered Ayurvedic wellness platform that democratizes access to holistic healthcare. The platform seamlessly integrates three core pillars:
1. AI Voice Assistant
Users can engage in natural, conversational exchanges with an intelligent wellness advisor powered by VAPI. Available 24/7, the assistant provides personalized Ayurvedic guidance without the barrier of cost or availability. It's trained to provide authentic Ayurvedic advice while being accessible to modern users.
2. Practitioner Appointment System
A complete booking and management system that connects users with certified Ayurvedic doctors. Users can:
- View available practitioners and their schedules
- Book appointments with conflict detection
- Receive confirmation emails automatically
- Track their consultation history
- Manage upcoming appointments
The system handles complex scenarios like preventing double-bookings, checking doctor availability, and automatically notifying both patient and practitioner.
3. Educational Video Library
A curated collection of Ayurvedic wellness videos with professional streaming capabilities. Users can:
- Browse wellness content
- Stream videos with adaptive quality
- Resume from where they left off
- Autogenerated thumbnail by Mux
This isn't just video hosting—it's a complete learning journey with progress tracking and a personalized experience.
Additional Features
- User Dashboard - Wellness journey overview, completed visits tracking, upcoming appointments
- Admin Dashboard - Doctor management, video uploads, appointment analytics, performance metrics
- Dark Mode UI - Optimized for comfortable viewing
Project Structure
https://github.com/devandop/ayur-ai
ayurai/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── dashboard/ # User dashboard + video library
│ │ ├── admin/ # Admin dashboard
│ │ ├── appointments/ # Booking system
│ │ └── voice/ # AI assistant
│ ├── components/
│ │ ├── ui/ # Radix + shadcn components
│ │ ├── dashboard/ # Dashboard components
│ │ └── admin/ # Admin components
│ ├── hooks/ # React Query hooks
│ └── lib/actions/ # Server actions
│
├── backend/
│ ├── src/api/ # Motia workflow steps
│ │ ├── create-appointment.step.ts
│ │ ├── admin-add-video.step.ts
│ │ └── ...
│ └── src/events/ # Event handlers
│
└── prisma/schema.prisma # Database schema
Running Locally
# Frontend
npm install
npm run dev
# Backend (separate terminal)
cd backend
npm install
npm run dev
Required environment variables: DATABASE_URL, CLERK_SECRET_KEY, MUX_TOKEN_ID, MUX_TOKEN_SECRET
The Story Behind It
The Inspiration
Ayurveda has guided millions toward wellness for over 5,000 years. It's a sophisticated medical system that views health holistically—not as the absence of disease, but as a state of balance in body, mind, and consciousness.
Yet here we are in 2025, and accessing Ayurvedic wisdom remains surprisingly difficult:
The Pain Points:
- Scarcity - Qualified Ayurvedic practitioners are rare and concentrated in certain regions
- Cost - Consultations are expensive, often unaffordable for the average person
- Inconvenience - Scheduling requires travel, time off work, and planning
- Fragmentation - Knowledge is scattered across books, blogs, and individual practitioners
- Impermanence - Guidance comes and goes—there's no continuous journey tracking
I wanted to solve this. Not by replacing traditional Ayurveda, but by making it accessible.
The Realization
I noticed that successful health platforms share a pattern:
- Instant Access - Users get help immediately (not days later)
- Professional Backup - AI is great, but human expertise matters
- Education - Users learn and take ownership of their health
- Continuity - Progress is tracked and visible over time
AyurAI combines all four.
Technical Highlights
The Challenge: Building a Robust Backend
Traditional backend architecture would require:
Routes → Controllers → Services → Database
↓
Manual event handling
↓
Complex error recovery
↓
Tightly coupled code
The Solution: Event-Driven Workflows with Motia
Isolated Workflow Steps
↓
Declarative Event Emission
↓
Automatic Event Orchestration
↓
Self-Documenting Code
Why Motia Was The Right Choice
1. Declarative, Self-Documenting Workflows
Instead of buried business logic, each workflow is clear and explicit:
export const config: ApiRouteConfig = {
type: 'api',
name: 'CreateAppointment',
path: '/api/appointments',
method: 'POST',
emits: ['appointment.created'], // ← Clear what this produces
flows: ['appointment-management'], // ← Declares the flow it belongs to
middleware: [
errorHandlerMiddleware, // ← Built-in error handling
clerkAuthMiddleware, // ← Authentication
RateLimiters.moderate, // ← Rate limiting
SanitizationPresets.medicalNotes, // ← Input sanitization
],
}
Anyone reading this knows exactly what the step does, what security is applied, and what events it produces.
2. Event-Driven Orchestration
When an appointment is created, instead of manually writing callback chains:
// With Motia - it's just one line:
await ctx.emit({
topic: 'appointment.created',
data: {
appointmentId: appointment.id,
patientEmail: appointment.user.email,
doctorName: appointment.doctor.name,
date: appointment.date,
time: appointment.time,
}
})
Motia automatically triggers:
- Email notifications (via separate event handler)
- Analytics tracking (via separate event handler)
- Notification caching (via separate event handler)
- Any future handlers we add (no code changes needed)
Zero manual coordination.
3. Built-in Middleware Stack
Motia provides production-grade middleware out of the box:
Authentication Middleware:
export const clerkAuthMiddleware: ApiMiddleware = async (req, ctx, next) => {
const clerkUserId = req.headers['x-clerk-user-id']
const user = await prisma.user.upsert({
where: { clerkId: clerkUserId },
update: { email, firstName, lastName },
create: { clerkId: clerkUserId, email, firstName, lastName }
})
;(ctx as any).user = user
return await next()
}
Rate Limiting:
const rateLimiter = createRateLimiter({
maxRequests: 30,
windowSeconds: 60,
keyExtractor: (req, ctx) => {
const user = (ctx as any).user
return user?.id || 'anonymous'
}
})
Input Sanitization:
const sanitizeString = (str: string, options = {}) => {
if (typeof str !== 'string') return str
let sanitized = removeControlCharacters(str)
sanitized = trimAndLimit(sanitized, options.maxLength || 1000)
sanitized = options.stripHTML ? stripHTML(sanitized) : sanitizeHTML(sanitized)
return sanitized
}
These are applied consistently across all workflows.
4. Type-Safe Event Handling
Events are typed and validated using Zod:
const responseSchema = z.object({
id: z.string(),
date: z.string(),
status: z.enum(['CONFIRMED', 'COMPLETED', 'CANCELLED']),
notes: z.string().nullable(),
// ... more fields
})
export const config = {
responseSchema: {
200: responseSchema,
401: z.object({ error: z.string() }),
500: z.object({ error: z.string() }),
}
}
If a response doesn't match the schema, it fails loudly. This catches bugs before they hit production.
5. Scalability Through Event Queuing
Using BullMQ under the hood, events are queued and processed reliably:
Event Emission
↓
BullMQ Queue
↓
Event Processors
↓
Database Updates / Emails / Analytics
If an event handler fails, it automatically retries with exponential backoff.
6. Visual Debugging with Motia Workbench
The Motia Workbench provides a visual interface showing:
- All workflow steps and their connections
- Real-time event flows
- Failed workflows with stack traces
- Performance metrics
- Request/response logs
Instead of blindly searching through logs, you can see exactly what happened.
Real-World Example: Appointment Booking Flow
Here's how AyurAI handles a complete appointment booking:
1. User submits appointment form
↓
2. POST /api/appointments hits CreateAppointment step
↓
3. Validation layer (Zod schema)
↓
4. Auth middleware (verify user)
↓
5. Rate limiting (max 30 bookings/minute)
↓
6. Sanitization (clean user input)
↓
7. Database checks:
- Doctor exists and is active?
- Time slot available?
- User doesn't have conflict?
↓
8. Create appointment record
↓
9. Emit 'appointment.created' event
↓
10. Motia automatically triggers:
├─ Email to user (confirmation)
├─ Email to doctor (new appointment)
├─ Analytics tracking (appointment created)
└─ Cache invalidation (user appointments cache)
↓
11. Return success to frontend
↓
12. Frontend optimistically updates UI
↓
13. TanStack Query refetches and syncs
Not a single line of manual coordination code.
Comparison: Motia vs Traditional Express
| Aspect | Express | Motia |
|---|---|---|
| Boilerplate | Manual routes, controllers, services | Declarative steps |
| Events | Manual pub/sub setup | Built-in, automatic orchestration |
| Middleware | Write custom, apply manually | Built-in, composable |
| Error Handling | Write catch/finally blocks | Automatic retry logic |
| Scaling | Manage queues manually | BullMQ integrated |
| Documentation | Separate OpenAPI specs | Self-documenting config |
| Testing | Spy on internals | Test steps independently |
| Debugging | Log diving | Visual workflow tracing |
Use of Mux (Additional Prize Category Participants Only)
Most platforms use Mux only for video hosting. AyurAI goes much deeper.
1. Direct Upload Infrastructure
Instead of uploading videos to my server (which is slow and storage-intensive), admins upload directly to Mux:
// Step 1: Create upload URL
const upload = await mux.video.uploads.create({
new_asset_settings: {
playback_policy: ['public'],
},
})
// Returns: { id, url }
// Admin uploads to this URL directly
// Video never touches my servers
Benefits:
- Server bandwidth savings
- Faster uploads
- No storage costs
- Secure direct transfer
2. Automatic Asset Processing
Once uploaded, Mux automatically:
Encoding:
- Converts to VP9, H.264, H.265
- Multiple quality levels (720p, 1080p, 4K)
- Generates HLS and DASH streams
Thumbnail Generation:
- Extracts key frames
- Generates optimized thumbnails
- Available at:
https://image.mux.com/{playbackId}/thumbnail.jpg
// AyurAI automatically stores this URL
const video = await prisma.video.create({
data: {
title,
description,
muxAssetId: asset.id,
muxPlaybackId: asset.playback_ids[0].id,
thumbnailUrl: `https://image.mux.com/${asset.playback_ids[0].id}/thumbnail.jpg`,
duration: asset.duration,
}
})
3. Adaptive Bitrate Streaming
Mux serves the appropriate quality based on user's connection:
// All video URLs are HLS streams
<video
src={`https://stream.mux.com/${muxPlaybackId}.m3u8`}
controls
/>
// Mux automatically:
// - Detects user's bandwidth
// - Serves optimal quality
// - Switches mid-stream if connection changes
// - Provides smooth buffering
4. Progress Tracking Integration
I built a custom system on top of Mux playback:
// VideoPlayer component
const VideoPlayer = ({ videoId, muxPlaybackId, initialPosition }) => {
const videoRef = useRef()
const [currentTime, setCurrentTime] = useState(initialPosition)
// Track time updates
const handleTimeUpdate = () => {
setCurrentTime(videoRef.current.currentTime)
}
// Auto-save every 15 seconds (debounced to reduce API calls)
useEffect(() => {
if (!videoId || !duration) return
const trackingTimer = setTimeout(async () => {
await videoApi.updateProgress(videoId, currentTime, duration)
}, 15000)
return () => clearTimeout(trackingTimer)
}, [currentTime])
// Resume from where user left off
useEffect(() => {
if (videoRef.current && initialPosition > 0) {
videoRef.current.currentTime = initialPosition
}
}, [initialPosition])
return (
<video
ref={videoRef}
src={`https://stream.mux.com/${muxPlaybackId}.m3u8`}
onTimeUpdate={handleTimeUpdate}
controls
/>
)
}
This creates a YouTube-like experience where users always resume from where they left off.
5. Metadata Extraction
Mux provides metadata that AyurAI uses for the UI:
const asset = await mux.video.assets.retrieve(assetId)
// Extract key metadata
const metadata = {
duration: asset.duration, // ← Total video length
width: asset.width, // ← Video dimensions
height: asset.height,
fps: asset.frame_rate, // ← For quality indicators
createdAt: asset.created_at,
}
// AyurAI displays duration on video cards:
// "45:30" (45 minutes, 30 seconds)
6. Playback ID Strategy
Mux assigns each video a unique playback ID, separate from the asset ID. This is powerful:
// Asset ID (unique, server-side)
muxAssetId: "abc123def456"
// Playback ID (unique, client-safe)
muxPlaybackId: "ji1XhtoywRhdk402DJ9FT3IcQ4Q01HCneqqDm2OwPBdrQ"
// Users only see playback ID in URLs:
// https://stream.mux.com/ji1XhtoywRhdk402DJ9FT3IcQ4Q01HCneqqDm2OwPBdrQ.m3u8
// This means:
// - Asset IDs never exposed to clients
// - Can revoke playback IDs without deleting video
// - Fine-grained access control possible
7. Global CDN Delivery
Mux's infrastructure ensures:
User in India
↓
Closest Mux edge node
↓
Video streams with <100ms latency
↓
Adaptive quality based on connection
User in USA
↓
Different edge node
↓
Same experience, optimized for region
AyurAI doesn't worry about CDN setup—Mux handles it.
8. Real Example: Video Upload Workflow
Here's the complete flow when an admin uploads a wellness video:
// Step 1: Admin clicks upload
// Frontend calls: POST /api/admin/videos/upload
// Step 2: Backend creates Mux upload session
const upload = await mux.video.uploads.create({
new_asset_settings: {
playback_policy: ['public'],
},
})
// Returns upload URL
// Step 3: Admin uploads directly to Mux
// (File never touches AyurAI servers)
// Step 4: Backend polls for completion
const upload = await mux.video.uploads.retrieve(uploadId)
if (upload.status === 'ready') {
const asset = await mux.video.assets.retrieve(upload.asset_id)
// Step 5: Save metadata to database
const video = await prisma.video.create({
data: {
title: "Ayurvedic Morning Routine",
description: "...",
muxAssetId: asset.id,
muxPlaybackId: asset.playback_ids[0].id,
thumbnailUrl: `https://image.mux.com/${asset.playback_ids[0].id}/thumbnail.jpg`,
duration: asset.duration,
isPublished: true,
}
})
// Emit event
await ctx.emit({
topic: 'video.published',
data: { videoId: video.id }
})
}
// Step 6: Motia triggers downstream events
// - Update search index
// - Notify admin of completion
// - Generate recommended videos
Result: Netflix-quality video appears in the app within minutes of upload. Zero server infrastructure concerns.
Why This Matters
By using Mux beyond just hosting, AyurAI:
✅ Saves bandwidth - No video data on my servers
✅ Saves storage - Mux stores, I just reference
✅ Saves complexity - No encoding pipelines to manage
✅ Saves costs - Pay per video delivered, not per storage
✅ Provides reliability - Netflix-grade infrastructure
✅ Enables features - Progress tracking, adaptive streaming, global delivery
✅ Scales automatically - Handle 1 user or 1M users with same infrastructure
Top comments (2)
Nice project! The best thing is, Everything works conveniently with Motia.
Hey Ashita, Would love to walk through what you're building. Feel free to reach out any time!