ApplyAI helps job seekers tailor resumes, research companies, and practice interviews with AI — all in one workflow. The platform combines a modern web frontend, a Python API, managed auth/database/storage, and background workers for long-running AI tasks.
ApplyAI is a monorepo with a Next.js 14 frontend (apps/web) and a FastAPI backend (apps/api). The frontend handles authentication UI, resume management, and a real-time interview simulator. The backend exposes REST and WebSocket endpoints for resume ingestion, AI rewriting, company research, and interview sessions. Supabase provides PostgreSQL, Auth (email/password and OAuth), and Storage for user files.
Long-running work — resume tailoring, PDF compilation, and company research — is offloaded to Celery workers backed by Redis. The API enqueues tasks and returns immediately; workers call Anthropic Claude (claude-sonnet-4-6) for all LLM operations. In production, Nginx terminates TLS and reverse-proxies traffic to the web and API containers, with dedicated WebSocket upgrade support for live interview streaming.
┌─────────────────────────────────────────────────────────────────────────────┐
│ Browser (User) │
└──────────────────────────────────┬──────────────────────────────────────────┘
│ HTTPS / WSS
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Nginx (infra/nginx) — prod only │
│ :443 / → web | /api/ → api | /api/.../ws/ → api │
└───────────────┬──────────────────────────────────────┬────────────────────────┘
│ │
▼ ▼
┌───────────────────────────────┐ ┌────────────────────────────────────────┐
│ Next.js 14 (apps/web) │ │ FastAPI (apps/api) │
│ App Router · Tailwind │ │ REST + WebSocket │
│ Supabase Auth (client) │───▶│ Auth middleware · route handlers │
└───────────────┬───────────────┘ └───────────────┬───────────────┬────────┘
│ │ │
│ Supabase Auth │ enqueue │ read/write
▼ ▼ ▼
┌───────────────────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ Supabase │ │ Redis │ │ Celery Workers │
│ PostgreSQL · Auth · Storage │◀───│ (task broker) │──│ research │
└───────────────────────────────┘ └─────────────────────┘ │ resume │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Anthropic API │
│ claude-sonnet- │
│ 4-6 │
└──────────────────┘
Typical request flow: the user signs in via Supabase Auth in the browser, uploads a resume through the Next.js UI, and the frontend calls the FastAPI API with a JWT. The API stores metadata in Supabase, enqueues a Celery task for AI tailoring, and returns a job ID. The worker rewrites the resume, compiles a PDF, and stores the result. For interviews, the frontend opens a WebSocket to the API; tokens stream back character-by-character while the session state is persisted in Supabase.
| Tool | Version |
|---|---|
| Node.js | 20+ |
| Python | 3.12+ |
| Docker & Docker Compose | v2 |
| Supabase project | Free tier works |
| Anthropic API key | Required for all LLM calls |
You will also need an Anthropic API key and a Supabase project with Auth providers configured (email + Google OAuth recommended).
Follow these steps from the repository root (applyai/):
-
Clone and install dependencies
git clone <repository-url> cd applyai cd apps/web && npm install && cd ../.. cd apps/api && python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt && cd ../..
-
Configure environment variables
cp .env.example apps/api/.env cp .env.example apps/web/.env.local
Fill in all values in both files (see Environment Variables Reference below). For local dev, set:
# apps/web/.env.local NEXT_PUBLIC_API_URL=http://localhost:8000 REDIS_URL=redis://localhost:6379/0 -
Run the database schema
Open the Supabase SQL Editor for your project and paste the contents of
apps/api/db/schema.sql, then click Run. -
Start Redis
docker compose -f infra/docker-compose.yml up redis -d
-
Start the API
cd apps/api source .venv/bin/activate uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
Verify the API is healthy:
curl http://localhost:8000/health # Expected: {"status":"ok"} -
Start the Celery worker (new terminal)
cd apps/api source .venv/bin/activate celery -A app.workers.celery_app worker --loglevel=info -Q research,resume
-
Start the frontend (new terminal)
cd apps/web npm run dev -
Open the app
Navigate to http://localhost:3000, sign in, and explore the dashboard, resume, and interview pages.
| Variable | Service | Description |
|---|---|---|
SUPABASE_URL |
API | Supabase project URL (server-side) |
SUPABASE_ANON_KEY |
API | Supabase anonymous/public key |
SUPABASE_SERVICE_ROLE_KEY |
API | Supabase service role key (server-side only — never expose to the browser) |
OPENAI_API_KEY |
API | OpenAI API key (optional; reserved for future use) |
ANTHROPIC_API_KEY |
API | Anthropic API key for all Claude LLM calls |
REDIS_URL |
API, Celery | Redis connection string (e.g. redis://localhost:6379/0) |
NEXT_PUBLIC_SUPABASE_URL |
Web | Supabase project URL (browser-safe) |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Web | Supabase anonymous key (browser-safe) |
NEXT_PUBLIC_API_URL |
Web | Base URL of the FastAPI backend (e.g. http://localhost:8000) |
Security note: never commit
.envor.env.localfiles. The service role key must only appear inapps/api/.env.
Base URL (local): http://localhost:8000
All authenticated endpoints require a Supabase JWT in the Authorization header:
export TOKEN="your-supabase-access-token"curl http://localhost:8000/healthUpload a PDF or DOCX resume for parsing and storage.
curl -X POST http://localhost:8000/api/v1/resume/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/path/to/resume.pdf"Response:
{ "resume_id": "uuid" }Tailor an uploaded resume for a specific job application. Runs asynchronously via Celery.
curl -X POST http://localhost:8000/api/v1/resume/tailor \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"application_id": "app-uuid", "resume_id": "resume-uuid"}'Response:
{
"pdf_url": "https://signed-url...",
"gap_analysis": {
"missing_hard_skills": ["Kubernetes"],
"missing_methodologies": ["Agile"],
"title_alignment_score": 82,
"recommended_title": "Senior Software Engineer",
"ats_score": 78
}
}Start a new AI interview session for an application.
curl -X POST http://localhost:8000/api/v1/interview/session \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"application_id": "app-uuid", "session_type": "behavioral"}'Response:
{ "session_id": "uuid" }Valid session_type values: phone_screen, behavioral, technical.
Connect to the live interview stream. Replace {session_id} with the ID from the session creation response.
ws://localhost:8000/api/v1/interview/ws/{session_id}
Client → Server (on connect):
{ "type": "auth", "token": "supabase-jwt" }Client → Server (user message):
{ "type": "message", "content": "My answer..." }Server → Client (streaming):
{ "type": "delta", "delta": "Tell me about..." }
{ "type": "complete", "content": "Tell me about a time you led a project." }High-level steps for OCI, AWS, or any VPS:
-
Build Docker images
docker compose -f infra/docker-compose.prod.yml build
-
Push to a container registry
Tag and push the
apiandwebimages to Docker Hub, OCIR, ECR, or your registry of choice. Updatedocker-compose.prod.ymlimage references if needed. -
Configure SSL certificates
Place your TLS certificate and private key at:
infra/ssl/certs/applyai.crt infra/ssl/private/applyai.keyFor local testing, generate a self-signed pair:
mkdir -p infra/ssl/certs infra/ssl/private openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout infra/ssl/private/applyai.key \ -out infra/ssl/certs/applyai.crt \ -subj "/CN=yourdomain.com" -
Deploy the stack
docker compose -f infra/docker-compose.prod.yml up -d
Nginx listens on ports 80 (HTTP → HTTPS redirect) and 443 (TLS termination). All services communicate over the
applyai_netDocker network. -
Configure DNS
Point your domain's A record to the VPS public IP. Set
NEXT_PUBLIC_API_URLand Supabase redirect URLs to your production domain.
- Rate limiting on LLM endpoints
- Resume PDF upload to Supabase Storage CDN
- Mobile-responsive interview UI
- Code execution sandbox (Phase 3)
applyai/
├── apps/
│ ├── web/ # Next.js 14 frontend
│ └── api/ # FastAPI backend + Celery workers
├── infra/
│ ├── docker-compose.yml # Local development
│ ├── docker-compose.prod.yml # Production (Nginx + all services)
│ └── nginx/nginx.conf
└── packages/
└── shared-types/ # Shared TypeScript types