# Technical Document (LLD/HLD)
## HireGenius- AI-Powered Recruitment Automation System  

### πŸ“˜ Table of Contents

| # | Section |
|---|---------|
| 1 | [System Overview](#1-system-overview) |
| 2 | [Module Breakdown](#2-module-breakdown) |
| 3 | [API Design](#3-api-design) |
| 4 | [Database Design](#4-database-design) |
| 5 | [Business Workflow](#5-business-workflow) |
| 6 | [System Architecture](#6-system-architecture) |
| 7 | [Sequence Diagram](#7-sequence-diagram) |
| 8 | [Flow Diagram](#8-flow-diagram) |
| 9 | [Phase Two Enhancement](#9-phase-two-enhancements) |



### 1. System Overview

#### 1.1 🎯 Purpose  
Automate the recruitment pipeline from resume ingestion to candidate ranking using **NLP-based parsing**, **vector embeddings**, and **AI-powered re-ranking**.

---

#### 1.2 βš™οΈ Key Capabilities
- **Incremental Resume Ingestion:** Fetch new resumes from Gmail on schedule  
- **Automated Resume Parsing:** Extract structured data (skills, experience, education, contact)  
- **Semantic Matching:** Use embeddings to find similar candidates to job descriptions  
- **AI Re-Ranking:** Re-rank top candidates using GPT-4o-mini for better precision  
- **Dashboard:** HR views ranked candidates with explainability  

---

#### 1.3 🧰 Technology Stack
| Layer | Technology |
|--------|-------------|
| **Frontend** | React.js + Tailwind CSS |
| **Backend** | Node.js + Express.js |
| **Database** | PostgreSQL 15+ with pgvector extension |
| **AI/ML** | OpenAI GPT-4o-mini |
| **Embeddings** | OpenAI `text-embedding-3-small` |
| **Email Integration** | Gmail API with OAuth 2.0 |
| **Scheduler** | node-cron |

### 2. Module Breakdown

#### Module 1: Unified Resume Ingestion Module
**Responsibility:** Multi-source resume ingestion with pluggable adapters  

| Source Type      | Adapter              | Trigger Method        | Priority | Notes |
|-----------------|-------------------|--------------------|---------|-------|
| Gmail            | GmailAdapter        | Scheduled (cron)     | High    | Phase 1 implementation |
| Webhook          | WebhookAdapter      | Real-time (HTTP POST)| High    | Future source |
| Cloud Storage    | CloudStorageAdapter | Polling/Event-driven | Medium  | Future source |
| Direct Upload    | UploadAdapter       | On-demand (UI)       | Medium  | Future source |
| ATS Integration  | ATSAdapter          | API polling          | Low     | Future source |

---

###### Core Functions
```javascript
// Abstract base adapter
class ResumeSourceAdapter {
  async fetchResumes(config) β†’ Returns ResumeCollection
  async validateSource(config) β†’ Returns boolean
  async getSourceMetadata() β†’ Returns SourceInfo
}

// Unified ingestion controller
fetchResumesFromAllSources(jobId) β†’ Orchestrates all active sources
registerSource(jobId, sourceType, config) β†’ Activates new source
deactivateSource(jobId, sourceId) β†’ Stops fetching from source
getActiveSourcesForJob(jobId) β†’ Returns list of active sources
```
##### Phase 1: Gmail Module
**Responsibility:** Email integration and resume fetching  

**Functions:**  
- Authenticate with Gmail API using OAuth 2.0  
- Fetch emails with attachments based on filters (date, keywords, labels)  
- Download resume attachments (PDF, DOCX)  
- Track last fetch timestamp for incremental processing  

**Key Operations:**  
- `fetchNewEmails(afterDate, filters)` β†’ Returns array of email objects with attachments  
- `downloadAttachment(messageId, attachmentId)` β†’ Returns file buffer  
- `getLastFetchTime(jobId)` β†’ Returns timestamp of last successful fetch  

**External Dependencies:** Gmail API, Google OAuth 2.0

---

#### Module 2: Resume Parser Module
**Responsibility:** Extract structured data from resume text  

**Parsing Strategy:** Rule-based NLP with regex patterns  

**Name Extraction:** First line heuristics, capitalization patterns  
**Email Extraction:** Regex pattern `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`  
**Phone Extraction:** Regex for various formats (+1-XXX-XXX-XXXX, (XXX) XXX-XXXX)  
**Skills Extraction:** Keyword matching against predefined skill database  
**Experience Parsing:** Section detection (keywords: "experience", "work history") + date parsing  
**Education Parsing:** Section detection + degree/institution extraction  

**Functions:**  
- `extractText(fileBuffer, format)` β†’ Returns plain text from PDF/DOCX  
- `parseResume(resumeText)` β†’ Returns structured JSON object  
- `validateParsedData(data)` β†’ Returns boolean + error messages  
- `normalizeSkills(skillArray)` β†’ Returns deduplicated, lowercase skills  

**Output Format (JSON):**
```json
{
  "name": "John Doe",
  "email": "john@example.com",
  "phone": "+1-555-0123",
  "skills": ["javascript", "python", "sql"],
  "experience": [
    {
      "title": "Software Engineer",
      "company": "Tech Corp",
      "duration": "2020-2023",
      "description": "Built REST APIs..."
    }
  ],
  "education": [
    {
      "degree": "B.S. Computer Science",
      "institution": "MIT",
      "year": 2018
    }
  ],
  "total_experience_years": 5
}
```
---

#### Alternative AI Approach (Optional Enhancement)
Instead of rule-based parsing, use GPT-4o-mini with structured JSON output. The flow would be: extract plain text -> send it to GPT-4o-mini with a schema-enforced prompt -> receive structured JSON (name, email, phone, skills, experience, education, total years).

**Pros:**  
- Much higher accuracy across different resume formats and layouts  
- Handles inconsistent structures, missing sections, and varied wording  
- Reduces need for complex regex/heuristics  

**Cons:**  
- Incur API usage costs  
- Slightly higher latency (~2–5 seconds per resume, depending on length)  
- Requires API reliability and internet access
- 
---
#### Module 3: Embedding Module
**Responsibility:** Generate vector embeddings for semantic search  

**Embedding Strategy:**  
- Use OpenAI text-embedding-3-small model (1536 dimensions)  
- Embed full resume text (truncated to 8000 chars if needed)  
- Embed JD text once per job creation  
- Store vectors in PostgreSQL pgvector columns  

**Functions:**  
- `generateEmbedding(text)` -> Returns 1536-dimensional float array  
- `batchGenerateEmbeddings(textArray)` -> Returns array of vectors (for efficiency)  
- `calculateCosineSimilarity(vec1, vec2)` -> Returns similarity score (0-1)  

**Caching Strategy:**  
- Cache JD embeddings in memory (job lifecycle)  
- Resume embeddings stored permanently in database  

---

#### Module 4: Database Module
**Responsibility:** Data persistence and vector operations  

**Core Operations:**  
- CRUD for all entities (users, jobs, candidates, communications)  
- Vector similarity search using pgvector  
- Transaction management for atomic operations  
- Query optimization with proper indexing  

**Key Functions:**  
- `storeCandidate(candidateData)` -> Returns candidateId  
- `findSimilarCandidates(jdEmbedding, topK)` -> Returns top-K candidate IDs by cosine similarity  
- `updateCandidateScore(candidateId, score)` -> Updates match score  
- `getCandidatesByJob(jobId, filters)` -> Returns paginated candidate list  

**Vector Search Query (Conceptual):**  
- Use pgvector's `<=>` operator for cosine distance  
- Order by `1 - (jd_embedding <=> resume_embedding)` for similarity  
- Apply filters (status, score range) after vector search  

---

#### Module 5: Job Description Module
**Responsibility:** JD lifecycle management and candidate retrieval  

**Functions:**  
- `createJob(jobData)` -> Validates input, generates embedding, stores in DB  
- `getActiveJobs()` -> Returns list of jobs with status='active'  
- `findTopCandidates(jobId, topK)` -> Queries DB for top-K similar candidates  
- `closeJob(jobId)` -> Updates status, stops scheduled tasks  

**JD Processing Flow:**  
- HR submits JD text via UI  
- Validate JD (length, required fields)  
- Generate embedding for JD text  
- Store job with embedding in database  
- Schedule resume ingestion task (cron)  
- Return job ID to frontend  

**Embedding Generation:**  
- Extract key requirements from JD text  
- Generate single embedding vector (1536-dim)  
- Store in `jobs.jd_embedding` column  

---Business

#### Module 6: Ranking Module
**Responsibility:** AI-powered re-ranking of top candidates  

**Two-Stage Ranking:**  

**Stage 1: Vector Similarity (Fast)**  
- Use pgvector to retrieve top-50 candidates by cosine similarity  
- Initial filter based on semantic matching  
- Execution time: <100ms  

**Stage 2: AI Re-Ranking (Precise)**  
- Use GPT-4o-mini to re-rank top-50 -> top-10  
- Provide full context: JD text + parsed resume data  
- AI considers: skill match, experience relevance, education fit  
- Execution time: 2-3 seconds  

**Functions:**  
- `getTopKSimilar(jobId, k)` -> Returns top-K candidates from vector search  
- `reRankWithAI(candidates, jdText)` -> Returns re-ranked list with scores and reasoning  
- `calculateFinalScore(candidate, job)` -> Combines vector similarity + AI score  

**AI Re-Ranking Prompt Structure:**  
**System:** You are an expert technical recruiter evaluating candidate fit.  

**Input:**  
- Job Description: [JD text]  
- Candidates: [Array of parsed resume data]  

**Task:**  
Rank these candidates from best to worst fit. For each, provide:  
1. Rank position (1-N)  
2. Fit score (0-1)  Business
3. Brief reasoning (2-3 sentences)  

**Consider:**  
- Skill alignment with required skills  
- Experience level match  
- Domain relevance  
- Education requirements  

**Output Format:** JSON array ordered by rank  

**Scoring Algorithm:**  
- Initial Vector Similarity: `S_vec` (from pgvector)  
- AI Re-Ranking Score: `S_ai` (from GPT-4o-mini)  

**Final Score:** `Final Score = 0.4 Γ— S_vec + 0.6 Γ— S_ai`  

**Reasoning:** Vector similarity provides broad semantic match, AI re-ranking adds nuanced understanding of requirements. 

---

#### Module 7: Application Controller
**Responsibility:** Orchestrate workflow across modules  

**Core Workflows:**  

**Resume Processing Workflow**  
- **Trigger:** Cron job (every 4 hours)  
- **Steps:** Fetch emails β†’ Extract text β†’ Parse β†’ Embed β†’ Store  

**Candidate Ranking Workflow**  
- **Trigger:** HR requests candidates for a job  
- **Steps:** Vector search β†’ AI re-rank β†’ Return ranked list  

**Deduplication Workflow**  
- **Trigger:** Before storing new candidate  
- **Steps:** Hash resume text β†’ Check DB β†’ Skip if exists  

**Functions:**  
- `processNewResumes(jobId)` -> Orchestrates resume processing  
- `getRankedCandidates(jobId)` -> Orchestrates ranking workflow  
- `handleDuplication(resumeHash)` -> Checks and logs duplicates  

**Error Handling:**  
- Retry logic for API failures (3 attempts with exponential backoff)  
- Log all errors with context (job ID, resume ID, error message)  
- Continue processing remaining resumes on individual failures  

---

#### Module 8: HR UI Module
**Responsibility:** Provide API endpoints for frontend dashboard  

**API Endpoints:** Detailed in Section 3  

**Dashboard Views:**  
- Job listing with candidate counts  
- Candidate pipeline (New, Contacted, Replied, Interviewed)  
- Candidate detail with parsed data and match score  
- Communication history per candidate  

---

### 3. API Design

#### 3.1 Authentication Endpoints

| Method | Endpoint       | Request Body         | Response      | Description               |
|--------|----------------|-------------------|--------------|--------------------------|
| POST   | /api/auth/login | {email, password}  | {user, token} | User login, returns JWT   |
| POST   | /api/auth/logout | {token}           | {success: true} | Invalidate token          |
| GET    | /api/auth/me   | Headers: Authorization: Bearer <token> | {user} | Get current user info     |

**Authentication Flow:**  
- User submits credentials  
- Server validates against database (bcrypt password comparison)  
- Generate JWT with 24-hour expiry  
- Return token to client  
- Client includes token in all subsequent requests  

---

#### 3.2 Job Management Endpoints### 9. Phase Two Enhancements (AI-assisted Interview Scheduler)
824


| Method | Endpoint        | Request Body                               | Response                  | Description                                |
|--------|-----------------|-------------------------------------------|---------------------------|--------------------------------------------|
| POST   | /api/jobs       | {title, jd_text, start_date, end_date, min_score} | {job_id, jd_embedding, ...} | Create job, generates embedding            |
| GET    | /api/jobs       | Query: ?status=active&page=1&limit=20     | {jobs[], total, page}     | List jobs with pagination                   |
| GET    | /api/jobs/:id   | -                                         | {job, candidate_count}    | Get job details                             |
| PUT    | /api/jobs/:id   | {title?, jd_text?, min_score?}            | {success: true}           | Update job (re-generates embedding if JD changed) |
| DELETE | /api/jobs/:id   | -                                         | {success: true}           | Close job, stop cron tasks                  |

**Request Example (POST /api/jobs):**  
```json
{
  "title": "Senior Full Stack Developer",
  "jd_text": "We are seeking an experienced full stack developer with 5+ years of experience in React, Node.js, and PostgreSQL. The ideal candidate will have strong problem-solving skills and experience with microservices architecture...",
  "start_date": "2025-10-01",
  "end_date": "2025-12-31",
  "min_score": 0.70
}
```
**Response Example:**  
```json
{
  "job_id": "uuid-123",
  "title": "Senior Full Stack Developer",
  "jd_embedding": [0.123, -0.456, ...], // 1536 dimensions
  "status": "active",
  "created_at": "2025-10-02T10:00:00Z"
}
```
---

#### 3.3 Candidate Retrieval Endpoint### 9. Phase Two Enhancements (AI-assisted Interview Scheduler)
824
s
Business
| Method | Endpoint                     | Request Body                               | Response                  | Description                       |
|--------|------------------------------|-------------------------------------------|---------------------------|-----------------------------------|
| GET    | /api/jobs/:jobId/candidates  | Query: ?status=new&sort=score&page=1&limit=20 | {candidates[], total}    | Get ranked candidates for job     |
| GET    | /api/candidates/:id          | -                                         | {candidate, parsed_data, communications[]} | Get candidate details            |
| PUT    | /api/candidates/:id/status   | {status}                                  | {success: true}           | Update candidate status           |
| POST   | /api/candidates/:id/notes    | {note_text}                               | {note_id}                 | Add HR notes                      |

**Request Example (GET /api/jobs/:jobId/candidates):**  
```json
{
  "job_id": "uuid-123",
  "status": "new",
  "sort": "score",
  "page": 1,
  "limit": 20
}
```
**Response Example:**  
```json
{
  "candidates": [
    {
      "candidate_id": "uuid-456",
      "full_name": "John Doe",
      "email": "john@example.com",
      "match_score": 0.87,
      "status": "new",
      "parsed_data": {
        "skills": ["javascript", "react", "node.js", "postgresql"],
        "total_experience_years": 6
      },
      "ai_reasoning": "Strong technical alignment with 5/6 required skills. Experience exceeds minimum requirement.",
      "created_at": "2025-10-02T12:30:00Z"
    }
  ],
  "total": 45,
  "page": 1,
  "limit": 20
}

```
---
    
#### 3.4 Resume Processing Endpoints (Internal)

| Method | Endpoint                         | Request Body | Response                  | Description                       |
|--------|----------------------------------|-------------|---------------------------|-----------------------------------|
| POST   | /api/internal/process-resumes/:jobId | -           | {processed_count, errors[]} | Manually trigger resume processing (admin only) |
| GET    | /api/internal/processing-logs/:jobId | Query: ?status=success&page=1 | {logs[], total}           | View processing logs               |
    
---
#### 3.5 Dashboard & Analytics Endpoints

| Method | Endpoint                           | Request Body | Response                  | Description                   |
|--------|------------------------------------|-------------|---------------------------|-------------------------------|
| GET    | /api/dashboard/stats/:jobId        | -           | {total, by_status{}, avg_score} | Get job pipeline metrics      |
| GET    | /api/dashboard/timeline/:jobId     | Query: ?start_date&end_date | {timeline_data[]} | Candidate activity over time |
| GET    | /api/export/candidates/:jobId      | Query: ?format=csv | CSV file                 | Export candidates             |
### 9. Phase Two Enhancements (AI-assisted Interview Scheduler)
824

**Response Example (GET /api/dashboard/stats/:jobId):**  
```json
{
  "total_candidates": 120,
  "by_status": {
    "new": 45,
    "contacted": 50,
    "replied": 20,
    "interviewed": 3,
    "rejected": 2
  },
  "avg_match_score": 0.74,
  "top_skills": ["javascript", "python", "react"],
  "last_updated": "2025-10-02T14:00:00Z"
}
```
---
### 4. Database Design

#### 4.1 Entity Relationship Diagram (ERD)
![](./image-1762666823208.png)
---
    
#### 4.2 Table Specifications

##### Table: users
**Purpose:** Store HR user accounts

| Column        | Type         | Constraints                          | Description                   |
|---------------|-------------|--------------------------------------|-------------------------------|
| user_id       | UUID        | PRIMARY KEY, DEFAULT gen_random_uuid() | Unique user identifier        |
| email         | VARCHAR(255)| UNIQUE, NOT NULL                     | User email (login credential) |
| refresh_token | VARCHAR(255)|                                      | Refresh token for session     |
| access_token  | VARCHAR(255)|                                      | Access token for session      |
| full_name     | VARCHAR(255)| NOT NULL                              | User's full name              |
| role          | VARCHAR(50) | NOT NULL, DEFAULT 'recruiter'        | User role (admin, recruiter)  |
| created_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP             | Account creation time         |
| updated_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP             | Last update time              |

**Indexes:**  
- `idx_users_email` on `email` (for login queries)

---

##### Table: jobs
**Purpose:** Store job descriptions and automation settings

| Column        | Type         | Constraints                              | Description                           |
|---------------|-------------|------------------------------------------|---------------------------------------|
| job_id        | UUID        | PRIMARY KEY, DEFAULT gen_random_uuid()   | Unique job identifier                 |
| created_by    | UUID        | FOREIGN KEY REFERENCES users(user_id)   | Job creator                           |
| title         | VARCHAR(255)| NOT NULL                                 | Job title                              |
| jd_text       | TEXT        | NOT NULL                                 | Full job description                   |
| jd_embedding  | vector(1536)| NOT NULL                                 | Semantic embedding of JD               |
| start_date    | DATE        | NOT NULL                                 | Job posting start date                 |
| end_date      | DATE        | NOT NULL                                 | Job posting end date                   |
| status        | VARCHAR(50) | DEFAULT 'active', CHECK (status IN ('active', 'paused', 'closed')) Business| Job status |
| created_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                | Job creation time                      |
| updated_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                | Last update time                        |

**Indexes:**  
- `idx_jobs_status` on `status` (filter active jobs)  
- `idx_jobs_dates` on `(start_date, end_date)` (date range queries)  
- `idx_jd_embedding` on `jd_embedding USING hnsw (vector_cosine_ops)` (fast similarity search)  

---

##### Table: candidates
**Purpose:** Store candidate resumes and parsed data

| Column           | Type         | Constraints                                           | Description                                  |
|------------------|-------------|-----------------------------------------------------|----------------------------------------------|
| candidate_id     | UUID        | PRIMARY KEY, DEFAULT gen_random_uuid()             | Unique candidate identifier                  |
| full_name        | VARCHAR(255)| NOT NULL                                            | Candidate's full name                        |
| email            | VARCHAR(255)| NOT NULL                                            | Candidate's email                            |
| phone            | VARCHAR(50) |                                                     | Candidate's phone number                     |
| resume_text      | TEXT        | NOT NULL                                            | Full resume text (plain)                     |
| resume_embedding | vector(1536)| NOT NULL                                            | Semantic embedding of resume                 |
| parsed_data      | JSONB       |                                                     | Structured resume data (skills, experience, education) |
| resume_hash      | VARCHAR(64) | UNIQUE, NOT NULL                                   | SHA-256 hash for deduplication               |
| source           | VARCHAR(50) | DEFAULT 'email'                                    | Source of resume (email, upload)            |
| blocked        | BOOLEAN     | DEFAULT FALSE                     | Candidate-job status |
| created_at       | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                          | Resume received time                          |
| updated_at       | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                          | Last update time                              |

**Indexes:**  
- `idx_candidates_email` on `email` (find by email)  
- `idx_candidates_hash` on `resume_hash` (deduplication check)  
- `idx_resume_embedding` on `resume_embedding USING hnsw (vector_cosine_ops)` (vector search)

**JSONB Structure (parsed_data):**  
```json
{
  "skills": ["javascript", "python", "react"],
  "experience": [
    {
      "title": "Software Engineer",
      "company": "Tech Corp",
      "duration": "2020-2023",
      "description": "Built microservices..."
    }### 9. Phase Two Enhancements (AI-assisted Interview Scheduler)
824

  ],
  "education": [
    {
      "degree": "B.S. Computer Science",
      "institution": "MIT",
      "year": 2018
    }
  ],
  "total_experience_years": 5
}
```

##### Table: job_candidate_matches
**Purpose:** Store matches between jobs and candidates (many-to-many relationship)

| Column        | Type         | Constraints                              | Description                     |
|---------------|-------------|------------------------------------------|---------------------------------|
| match_id      | UUID        | PRIMARY KEY, DEFAULT gen_random_uuid()   | Unique match identifier         |
| job_id        | UUID        | FOREIGN KEY REFERENCES jobs(job_id)      | Associated job                  |
| candidate_id  | UUID        | FOREIGN KEY REFERENCES candidates(candidate_id) | Associated candidate           |
| match_score   | DECIMAL(5,4)|                                          | AI-generated match score (0-1) |
| ai_reasoning  | TEXT        |                                          | AI explanation for match        |
| status        | VARCHAR(50) | DEFAULT 'new', CHECK (status IN ('new','contacted','replied','interviewed','rejected','hired')) | Candidate-job status |
| created_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                | Match creation time             |
| updated_at    | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP                | Last update time                |

**Indexes:**  
- `idx_matches_job` on `job_id`  
- `idx_matches_candidate` on `candidate_id`  
- `idx_matches_score` on `(job_id, match_score DESC)` (ranked retrieval)

---
##### Table: managers
    
**Purpose:** Store manager profiles responsible for interviews or job coordination.

| Column     | Type         | Constraints                            | Description                       |
| ---------- | ------------ | -------------------------------------- | --------------------------------- |
| id         | UUID         | PRIMARY KEY, DEFAULT gen_random_uuid() | Unique manager identifier         |
| name       | VARCHAR(255) | NOT NULL                               | Manager's full name               |
| email      | VARCHAR(255) | UNIQUE, NOT NULL                       | Manager's email address           |
| skills     | JSONB        | DEFAULT '[]'                           | List of skills or expertise areas |
| department | VARCHAR(255) |                                        | Department or function area       |
| created_at | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP              | Record creation time              |
| updated_at | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP              | Record last update time           |

**Indexes:**

- `idx_managers_email` on `email`
---
#### Table: manager_availabilities
    
**Purpose:** Track availability slots of managers for scheduling interviews.
    
| Column          | Type      | Constraints                                           | Description                           |
| --------------- | --------- | ----------------------------------------------------- | ------------------------------------- |
| availability_id | UUID      | PRIMARY KEY, DEFAULT gen_random_uuid()                | Unique availability record identifier |
| manager_id      | UUID      | FOREIGN KEY REFERENCES managers(id) ON DELETE CASCADE | Manager reference                     |
| date            | DATE      | NOT NULL                                              | Available dBusinessate                        |
| start_time      | TIME      | NOT NULL                                              | Start time of availability            |
| end_time        | TIME      | NOT NULL                                              | End time of availability              |
| is_booked       | BOOLEAN   | DEFAULT false                                         | Marks if slot is already booked       |
| created_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP                             | Record creation time                  |
| updated_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP                             | Record last update time               |

**Indexes:**

- `idx_manager_availabilities_manager_id` on `manager_id`
- `idx_manager_availabilities_date` on `date`
---
##### Table: job_managers
    
**Purpose:** Link managers to specific jobs with defined roles (e.g., hiring manager, interviewer).
    
| Column     | Type         | Constraints                                           | Description                                       |
| ---------- | ------------ | ----------------------------------------------------- | ------------------------------------------------- |
| id         | UUID         | PRIMARY KEY, DEFAULT gen_random_uuid()                | Unique record identifier                          |
| job_id     | UUID         | FOREIGN KEY REFERENCES jobs(job_id) ON DELETE CASCADE | Job reference                                     |
| manager_id | UUID         | FOREIGN KEY REFERENCES managers(id) ON DELETE CASCADE | Manager reference                                 |
| role       | VARCHAR(100) | NOT NULL                                              | Role in hiring process (e.g., interviewer, owner) |
| created_at | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP                             | Record creation time                              |
| updated_at | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP                             | Record last update time                           
    
- `idx_job_managers_job_id` on `job_id`
- `idx_job_managers_manager_id` on `manager_id` 
---
##### Table: communicationBusiness
    
**Purpose:** Stores all communication exchanges between the system, candidates, and managers- including interview invites, responses, and final confirmations.

| Column            | Type         | Constraints                               | Description                                                                                                                                                                         |
| ----------------- | ------------ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `comm_id`         | UUID         | PK                                        | Unique communication identifier                                                                                                                                                     |
| `candidate_id`    | UUID         | FK β†’ `candidates.candidate_id`, `CASCADE` | Candidate reference                                                                                                                                                                 |
| `job_id`          | UUID         | FK β†’ `jobs.job_id`, `SET NULL`            | Associated job                                                                                                                                                                      |
| `manager_id`      | UUID         | FK β†’ `managers.id`, `SET NULL`            | Manager reference                                                                                                                                                                   |
| `type`            | ENUM         | NOT NULL                                  | Type of communication:<br>β€’ `candidate_invite`<br>β€’ `candidate_response`<br>β€’ `manager_invite`<br>β€’ `manager_response`<br>β€’ `final_confirmation`                                    |
| `subject`         | VARCHAR(500) |                                           | Email subject line                                                                                                                                                                  |
| `body`            | TEXT         |                                           | Full email/message content                                                                                                                                                          |
| `slot_date`       | DATE         | NULLABLE                                  | Date of the interview slot                                                                                                                                                          |
| `slot_start_time` | TIME         | NULLABLE                                  | Start time of the slot                                                                                                                                                              |
| `slot_end_time`   | TIME         | NULLABLE                                  | End time of the slot                                                                                                                                                                |
| `unique_hash`     | STRING       | NULLABLE                                  | Hash used to prevent duplicate communications for same slot                                                                                                                         |
| `status`          | ENUM         | DEFAULT: `pending_candidate_response`     | Current communication state:<br>β€’ `pending_candidate_response`<br>β€’ `candidate_confirmed`<br>β€’ `pending_manager_response`<br>β€’ `manager_confirmed`<br>β€’ `confirmed`<br>β€’ `declined` |
| `gmail_thread_id` | STRING       |                                           | Gmail thread tracking ID                                                                                                                                                            |
| `sent_at`         | TIMESTAMP    |                                           | When email was sent                                                                                                                                                                 |
| `received_at`     | TIMESTAMP    |                                           | When response was received                                                                                                                                                          |
| `created_at`      | TIMESTAMP    | DEFAULT: `CURRENT_TIMESTAMP`              | Record creation timestamp                                                                                                                                                           |
| `updated_at`      | TIMESTAMP    | DEFAULT: `CURRENT_TIMESTAMP`              | Record last updated timestamp                                                                                                                                                       |

**Indexes:**  
- `idx_communication_status` on `status`
- `idx_communication_candidate_id` on `candidate_id`
- `idx_communication_manager_id` on `manager_id`
- `idx_communication_job_id` on `job_id`
- `idx_communication_gmail_thread_id` on `gmail_thread_id`
---
##### Table: interview_schedule
    
**Purpose:** Maintains the record of interview schedules created for candidates against specific jobs and managers.
Each record represents a confirmed or proposed interview slot with associated meeting details.
Prevents scheduling conflicts for the same candidate in overlapping slots.
    
| **Column**          | **Type**                                                                 | **Constraints**                                                           | **Description**                                 |
| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ----------------------------------------------- |
| `interview_id`      | `UUID`                                                                   | **PK**, Default: `UUIDV4`                                                 | Unique identifier for each interview            |
| `job_id`            | `UUID`                                                                   | **FK β†’ jobs.job_id**, `CASCADE` on delete/update                          | Associated job for which interview is scheduled |
| `candidate_id`      | `UUID`                                                                   | **FK β†’ candidates.candidate_id**, `CASCADE` on delete/update              | Candidate being interviewed                     |
| `manager_id`        | `UUID`                                                                   | **FK β†’ managers.id**, `CASCADE` on delete/update                          | Manager conducting or managing the interview    |
| `comm_id`           | `UUID`                                                                   | **FK β†’ communication.comm_id**, `SET NULL` on delete, `CASCADE` on update | References the related communication thread     |
| `slot_date`         | `DATEONLY`                                                               | **NOT NULL**                                                              | Date of the scheduled interview                 |
| `slot_start_time`   | `TIME`                                                                   | **NOT NULL**                                                              | Interview start time                            |
| `slot_end_time`     | `TIME`                                                                   | **NOT NULL**                                                              | Interview end time                              |
| `meeting_link`      | `STRING(500)`                                                            | NULLABLE                                                                  | Meeting URL (Google Meet, Zoom, Teams, etc.)    |
| `meeting_platform`  | `ENUM('google_meet', 'zoom', 'teams')`                                   | **NOT NULL**, Default: `google_meet`                                      | Platform used for the meeting                   |
| `calendar_event_id` | `STRING(255)`                                                            | NULLABLE                                                                  | External calendar event identifier              |
| `status`            | `ENUM('proposed', 'scheduled', 'rescheduled', 'cancelled', 'completed')` | **NOT NULL**, Default: `proposed`                                         | Current interview status                        |
| `created_by`        | `UUID`                                                                   | **FK β†’ users.user_id**, `SET NULL` on delete, `CASCADE` on update         | User who created the interview record           |
| `notes`             | `TEXT`                                                                   | NULLABLE                                                                  | Optional notes or remarks                       |
| `created_at`        | `TIMESTAMP`                                                              | Default: `CURRENT_TIMESTAMP`                                              | Record creation timestamp                       |
| `updated_at`        | `TIMESTAMP`                                                              | Default: `CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`                  | Record last updated timestamp                   |

**Indexes:**  
- `idx_interview_job_id` on `job_id`
- `idx_interview_candidate_id` on `candidate_id`
- `idx_interview_manager_id` on `manager_id`
- `idx_interview_slot_date` on `slot_date`
- `idx_interview_status` on `status`
    
---
##### Table: templates
**Purpose:** Store user-specific email templates for communication

| Column       | Type         | Constraints                          | Description                           |
|--------------|-------------|--------------------------------------|---------------------------------------|
| template_id  | UUID        | PRIMARY KEY, DEFAULT gen_random_uuid() | Unique template identifier           |
| created_by   | UUID        | FOREIGN KEY REFERENCES users(user_id) | User who created the template        |
| name         | VARCHAR(255)| NOT NULL                              | Template name                        |
| type         | VARCHAR(50) | NOT NULL, CHECK (type IN ('outreach','follow_up','reply','manual')) | Type of template                     |
| subject      | VARCHAR(500)| NOT NULL                              | Email subject template               |
| body         | TEXT        | NOT NULL                              | Email body template                  |
| created_at   | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP             | Template creation time               |
| updated_at   | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP             | Last update time                     |

**Indexes:**  
- `idx_templates_user` on `created_by` (fetch templates by user)  
- `idx_templates_type` on `type` (filter templates by type)  

---    
    
#### 4.3 Database Relationships

| Parent Table | Child Table           | Relationship Type | Foreign Key                                     | On Delete |
|--------------|---------------------|-----------------|-----------------------------------------------|-----------|
| users        | jobs                | One-to-Many      | jobs.created_by β†’ users.user_id               | SET NULL  |
| users        | templates           | One-to-Many      | templates.created_by β†’ users.user_id          | CASCADE   |
| jobs         | job_candidate_matches | One-to-Many    | job_candidate_matches.job_id β†’ jobs.job_id    | CASCADE   |
| candidates   | job_candidate_matches | One-to-Many    | job_candidate_matches.candidate_id β†’ candidates.candidate_id | CASCADE |
| candidates   | communication       | One-to-Many      | communication.candidate_id β†’ candidates.candidate_id | CASCADE |
| templates    | communication       | One-to-Many      | communication.template_id β†’ templates.template_id | SET NULL |

**Cascade Deletion Logic:**  
- When a job is deleted, all associated job-candidate matches are deleted  
- When a candidate is deleted, all associated job-candidate matches and communications are deleted  
- When a user is deleted, their jobs remain but `created_by` is set to NULL  
- When a user is deleted, all their templates are deleted  
- When a template is deleted, communications referencing it will have `template_id` set to NULL  

---

#### 4.4 pgvector Configuration
### 9. Phase Two Enhancements (AI-assisted Interview Scheduler)
824

**Extension Setup:**  Business
```sql
CREATE EXTENSION IF NOT EXISTS vector;
```
    
---
    
### 5. Business Workflow

##### PHASE 1: JOB CREATION
- HR User opens Dashboard β†’ Create Job Form
  - Input: Title, JD Text, Dates, Min Score
- POST /api/jobs
  - Validate input (dates, text length)
  - Generate JD embedding (OpenAI API)
  - Store in database
  - Schedule cron job (resume ingestion every 4 hours)
- Job Created βœ…
  - Status: Active

##### PHASE 2: RESUME INGESTION (Automated - Every 4 hours)
- Cron Trigger β†’ Application Controller
- Gmail Module:
  - Fetch emails with attachments
  - Filter: after last fetch, keywords (resume, application)
  - Download attachments (PDF/DOCX)
- Resume Parser Module:
  - Extract text from PDF/DOCX
  - Generate SHA-256 hash
  - Check deduplication (processing_logs)
  - If duplicate β†’ skip & log as 'duplicate'
  - If new β†’ continue processing
- Parse Resume (Rule-based NLP):
  - Extract: Name, Email, Phone
  - Extract: Skills
  - Extract: Experience
  - Extract: Education
  - Calculate total experience years
- Structured JSON created: {name, email, skills[], experience[], education[], total_years}
- Embedding Module:
  - Generate embedding (1536-dim vector)
- Database Module:
  - Store candidate record:
    - parsed_data (JSONB)
    - resume_embedding
    - resume_hash
    - status: 'new'
  - Log processing in processing_logs:
    - status: 'success'
    - processing_time_ms
- Candidate Stored βœ…

##### PHASE 3: CANDIDATE RANKING (On-Demand)
- HR User β†’ Dashboard β†’ View Job β†’ Click "View Candidates"
- GET /api/jobs/:jobId/candidates
- Application Controller:
  - Ranking Module (Stage 1: Vector Search):
    - Fetch job.jd_embedding
    - Query top-50 candidates by cosine similarity
  - Ranking Module (Stage 2: AI Re-Ranking):
    - Prepare AI context (JD text + top-50 candidates)
    - Call GPT-4o-mini:
      - Rank candidates (1-50)
      - Fit score (0-1)
      - Brief reasoning
    - Calculate final_score = 0.4 Γ— vector_sim + 0.6 Γ— AI_score
    - Update candidates.match_score and ai_reasoning in DB
- Return Ranked Candidates β†’ Frontend
- HR Dashboard displays Top-10 candidates:
  - Name, Email, Match Score
  - AI reasoning
  - Parsed Data (skills, experience)
  - Actions: View Details, Contact, Reject

##### PHASE 4: CANDIDATE INTERACTION (Manual)
- HR selects candidate β†’ Actions:
  - View full resume
  - Update status ('contacted', 'replied', 'interviewed')
  - Add notes (stored in notes table)
  - Send email (logged in communication table)
- Status updated in database
- Dashboard reflects new status

##### PHASE 5: JOB CLOSURE
- HR closes job β†’ DELETE /api/jobs/:jobId
  - Update job.status = 'closed'
  - Stop all scheduled tasks
  - Archive candidates (records remain in DB)
- Job Closed βœ…
    
---
    
### 6. System Architecture
![](./image-1759857600801.drawio.png)
---

### 7. Sequence Diagram
![](./image-1759857739651.-2025-10-07-172157.png)
---

### 8. Flow Diagram
![](./image-1759914896125.drawio.png)
---

### 9. Phase Two Enhancements
#### AI-assisted interview scheduler
- πŸ§‘β€πŸ’Ό 1. Manager & Availability Setup

  - HR assigns one or more managers to each job.

  - Managers define their available time slots for interviews.

  - System ensures no overlapping interview slots.

- 🎯 2. Candidate Invitation

  - HR shortlists candidates for interviews.

  - AI automatically drafts and sends invitation emails with available time slots.

  - Each invitation is recorded for tracking and status updates.

- πŸ“¬ 3. Candidate Response

  - The system listens for candidate replies.

  - AI interprets their response (acceptance, decline, or preferred slot).

  - Once confirmed, the selected slot is blocked to prevent double booking.

- πŸ‘¨β€πŸ’Ό 4. Manager Confirmation

  - The system notifies the respective manager about the candidate’s selected slot.

  - Manager confirms or reschedules based on their convenience.

  - Their response is automatically updated in the system.

- πŸ—“οΈ 5. Interview Scheduling

  - Once both sides confirm, the interview is officially scheduled.

  - A meeting link (Google Meet / Zoom) is generated automatically.

  - Confirmation emails are sent to all participants β€” HR, Manager, and Candidate.

- πŸ“Š 6. Post-Interview Actions

  - After the interview, HR can mark it as completed.

  - Feedback and notes can be added for future reference.

  - The system reopens or manages availability slots for upcoming interviews.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9