An AI-powered omnichannel ticket management system that automatically classifies, responds to, and routes support tickets across Email and Instagram Direct.
Live demo: https://helpdesk-u64v.onrender.com
The demo runs on Render's free tier, so the first request after a period of inactivity can take 30β60 seconds while the service wakes up. See Known Limitations for what does and doesn't work on the live demo.
Demo Login
When you open the site you will be asked to log in. Use these credentials to access the dashboard:
Field Value Email admin@example.com Password password123
These are demo-only credentials for the live deployment. Please don't change the password or delete users.
- Inbound Processing: Ingest incoming support emails via SendGrid Inbound Parse or IMAP email listener (
imapflow/mailparser). - Thread Management: Full email conversation threads with bidirectional agent and customer replies.
- Outbound Notifications: Automated and agent-composed replies dispatched via SMTP (
nodemailer) and SendGrid.
- Instagram Graph API Integration: Ingest Direct Messages (DMs) from customers in real time.
- Customer Social Insights: View Instagram handles, profile avatars, follower counts, bio, and verification badges right in the conversation sidebar.
- Bi-Directional Messaging: Agents can draft and dispatch replies directly to the customer's Instagram inbox from the helpdesk.
- In-Browser DM Simulator: Test incoming Instagram conversations with custom scenarios using Simulate Inbound DM, without needing an active Meta webhook setup.
- Built-in Setup Guide: Interactive modal providing step-by-step Meta Developer App configuration instructions.
- Automatic Classification: Classify incoming tickets into categories (General Question, Technical Question, Refund Request, Admission, Complaint, etc.).
- Smart Summaries & Suggested Replies: AI drafts context-aware responses incorporating knowledge base articles.
- Confidence Scoring & Sentiment Analysis: Track ticket sentiment (positive, neutral, frustrated) and model confidence (0β100%).
- Autonomous Resolution & Escalation:
- Auto-resolves routine queries when confidence meets threshold.
- Automatically flags and escalates complex queries (e.g., high refund requests, system bugs) with documented escalation reasons.
- AI Status Tracking: Real-time lifecycle badges (
ai_suggested,ai_resolved,escalated,human_replied,awaiting_reply).
- Server-Sent Events (SSE): Instant browser updates for new tickets, incoming customer replies, and status transitions without page refreshes.
- Role-Based Access Control:
- Admin: Full access, user management, omnichannel I-Tickets, role assignment.
- Agent: Ticket queue, reply drafting, assignment, and status updates.
- Filtering & Search: Filter by status, priority, channel, sentiment, category, and assigned agent.
- Analytics Dashboard: Aggregated overview of open tickets, AI resolution rates, sentiment breakdown, and ticket volume.
The live demo at https://helpdesk-u64v.onrender.com is hosted on Render's free tier, which causes the following:
- Outbound email replies are not delivered. Since September 2025, Render's free web services block outbound traffic on SMTP ports 25, 465 and 587. Tickets are still created from incoming emails (IMAP uses port 993, which is not blocked) and AI replies are generated and saved to the ticket, but the reply email itself is never sent to the customer's inbox. Fixes: upgrade the web service to a paid instance (ports 465 and 587 become available), or send through an HTTPS email API (Brevo, SendGrid, Resend) instead of SMTP.
- Instagram Direct is not live-connected. The Meta app is in development mode, so real customer DMs are not delivered to the webhook. The Instagram flow can be tested with Simulate Inbound DM inside the I-Tickets page, which creates a conversation exactly as an incoming DM would.
- The service sleeps when idle. The IMAP listener, Instagram poller and job queue pause while the free instance is asleep, so emails can be picked up late.
- The free Render Postgres database expires 30 days after creation.
All of these are hosting-tier limits, not application bugs. Locally, with SMTP access and a live Meta app, the full email and Instagram flows work.
- Frontend: React 19, TypeScript, Vite, Tailwind CSS v4, shadcn/ui, Radix UI, TanStack Query, Lucide Icons
- Backend: Express 5, TypeScript, Bun runtime
- Database & ORM: PostgreSQL, Prisma ORM (with
@prisma/adapter-pg) - AI: Google Gemini (via Vercel AI SDK)
- Authentication: Better Auth (session-based authentication with RBAC)
- Live Updates: Server-Sent Events (SSE)
- Job Queue: pg-boss
- Email Services: SendGrid (Inbound Webhooks), IMAP Listener (
imapflow), SMTP (nodemailer) - Social Messaging: Instagram Graph API
- Monitoring: Sentry (Frontend & Backend error tracking)
- Testing: Playwright (E2E), Vitest & Testing Library (Unit/Component)
- Hosting: Render (web service + managed PostgreSQL)
helpdesk/
βββ client/ # React frontend (Vite)
β βββ src/
β β βββ components/ # Shared UI & domain components
β β β βββ iticket/ # Instagram I-Ticket conversation & simulator UI
β β β βββ ui/ # shadcn/ui component library
β β βββ lib/ # Auth client, SSE hooks, theme, API services
β β βββ pages/ # Dashboard, Tickets, I-Tickets, Users, Login
β β βββ types/ # Frontend TypeScript definitions
β βββ vite.config.ts
βββ server/ # Express backend (Bun)
β βββ prisma/ # Prisma schema, migrations, and seed scripts
β β βββ schema.prisma # Database schema (Tickets, ITickets, Users, etc.)
β β βββ seed.ts # Admin & AI agent seed script
β β βββ seed-tickets.ts # Sample email tickets seed
β β βββ seed-replies.ts # Sample email replies seed
β β βββ seed-itickets.ts # Realistic Instagram I-Tickets seed
β βββ src/
β β βββ lib/ # Gemini AI, Instagram client, IMAP/SMTP, SSE, Sentry
β β βββ middleware/ # Auth, Admin guard, Webhook signature validation
β β βββ routes/ # REST endpoints (tickets, itickets, users, webhooks, events)
β βββ package.json
βββ core/ # Shared contracts (Zod schemas, constants, types)
βββ e2e/ # Playwright end-to-end test suite
βββ package.json # Workspace root
- Bun (v1.0+ recommended runtime & package manager)
- PostgreSQL (v14+)
- Google Gemini API key (for AI features)
- (Optional) Meta / Instagram App credentials (for live Instagram DMs)
- (Optional) SMTP / IMAP credentials (for live email processing)
bun installcp server/.env.example server/.envEdit server/.env with your local settings. Minimum required for local development:
DATABASE_URL="postgresql://user:password@localhost:5432/helpdesk?sslmode=disable"
BETTER_AUTH_SECRET="your-32-character-secret-key-here"
BETTER_AUTH_URL="http://localhost:3000"
TRUSTED_ORIGINS="http://localhost:5173"
GOOGLE_GENERATIVE_AI_API_KEY="your-gemini-api-key"
SEED_ADMIN_EMAIL="admin@helpdesk.com"
SEED_ADMIN_PASSWORD="YourSecurePassword123!"cd server
# Run migrations
bunx prisma migrate dev
# Seed Admin & AI agent accounts
bunx prisma db seed
# (Optional) Seed demo email tickets and Instagram I-Tickets
bun run prisma/seed-tickets.ts
bun run prisma/seed-replies.ts
bun run prisma/seed-itickets.tsRun the backend and frontend in separate terminals:
# Terminal 1 β Backend (Port 3000)
cd server && bun run dev
# Terminal 2 β Frontend (Port 5173)
cd client && bun run devNavigate to http://localhost:5173 and log in with the admin credentials you set in SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD.
# Run client unit & component tests (Vitest)
cd client && bun run test
# Run end-to-end tests (Playwright - requires dev servers running)
bun run test:e2e
# Run E2E tests with UI mode
bun run test:e2e:ui| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
BETTER_AUTH_SECRET |
32+ character secret for session signing | Yes |
BETTER_AUTH_URL |
Public base URL of the app (e.g. https://helpdesk-u64v.onrender.com) |
Yes |
TRUSTED_ORIGINS |
App origin allowed for CORS / auth (same as BETTER_AUTH_URL in production) |
Yes |
NODE_ENV |
Set to production to serve the built React client from the Express server |
Production |
GOOGLE_GENERATIVE_AI_API_KEY |
Gemini API key for ticket classification & replies | Yes |
SEED_ADMIN_EMAIL |
Default admin email generated during seeding | Yes |
SEED_ADMIN_PASSWORD |
Default admin password generated during seeding | Yes |
WEBHOOK_SECRET |
Secret token to authenticate incoming email webhooks | Optional |
INSTAGRAM_VERIFY_TOKEN |
Webhook verification token for Meta app | Optional |
INSTAGRAM_PAGE_ACCESS_TOKEN |
Meta Page access token with DM permissions | Optional |
INSTAGRAM_PAGE_ID |
Meta Page / Instagram Business account ID | Optional |
INSTAGRAM_APP_SECRET |
Meta App Secret for signature verification | Optional |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS |
SMTP configuration for outbound replies | Optional |
IMAP_HOST / IMAP_PORT / IMAP_USER / IMAP_PASS |
IMAP configuration for incoming email listener | Optional |
SENTRY_DSN / SENTRY_ENVIRONMENT |
Sentry error tracking credentials | Optional |
The app deploys as a single Render web service plus a Render PostgreSQL database. In production, the Express backend serves the pre-built React client from client/dist, so no separate frontend deployment is needed.
In Render, create a PostgreSQL instance in the same region as your web service and copy its Internal Database URL.
Create a Web Service from the GitHub repo and set:
| Setting | Value |
|---|---|
| Language / Runtime | Node (Bun is available on Render's native runtime) |
| Root Directory | (empty β repository root) |
| Build Command | bun install && bun run build |
| Start Command | cd server && bunx prisma migrate deploy && bun run start |
| Health Check Path | /api/health |
The build command builds the React client and generates the Prisma client. The start command applies pending migrations on every boot.
Add the variables from the reference table above, with these production values:
DATABASE_URLβ the Internal Database URL from step 1NODE_ENVβproductionBETTER_AUTH_URLandTRUSTED_ORIGINSβ your service URL, with no trailing slash
Render's free plan has no shell, so run the seed locally against the External Database URL (PowerShell shown):
cd server
$env:DATABASE_URL="<External Database URL>?sslmode=require"
bunx prisma db seed
Remove-Item Env:DATABASE_URLOn macOS/Linux: DATABASE_URL="<External Database URL>?sslmode=require" bunx prisma db seed
In the Meta Developer dashboard, set the webhook callback URL to https://<your-app>.onrender.com/api/webhooks/instagram and the verify token to the value of INSTAGRAM_VERIFY_TOKEN. Real DMs from ordinary users only arrive once the Meta app is published; until then, use Simulate Inbound DM.
Outbound SMTP is blocked on Render's free web services (see Known Limitations). To send reply emails in production, use a paid Render instance or an HTTPS email provider.