Talent-Acquisition-Platform

API Reference

Base URL: http://localhost:8000/api/v1
Interactive docs: http://localhost:8000/api/docs (Swagger UI)
Authentication: All endpoints (except /auth/login and /auth/refresh) require Authorization: Bearer <access_token>


Authentication

POST /auth/login

// Request
{ "email": "admin@p99soft.com", "password": "Admin@P99#2025" }

// Response 200
{
  "access_token": "eyJ...",
  "refresh_token": "...",
  "token_type": "bearer",
  "user": {
    "id": "uuid",
    "org_id": "uuid",
    "email": "admin@p99soft.com",
    "name": "P99soft Admin",
    "role": "super_admin",
    "permissions": ["candidates:read", "candidates:create", ...]
  }
}

POST /auth/refresh

{ "refresh_token": "..." }
// Returns same structure as /auth/login

POST /auth/logout

{ "refresh_token": "..." }
// Revokes the refresh token

GET /auth/me

Returns current user profile + permissions.

PUT /auth/profile

{ "name": "New Name", "password": "NewPass123", "department": "HR" }

Users

GET /users

List all users in the org. Requires users:read.

POST /users

{
  "email": "jane@company.com",
  "name": "Jane Smith",
  "password": "SecurePass123",
  "role": "recruiter",
  "department": "HR",
  "title": "Senior Recruiter"
}

Requires users:manage.

GET /users/{id}

PUT /users/{id}

DELETE /users/{id} — deactivates (soft delete)


Roles

GET /roles

List all roles (system + org custom). Requires roles:manage.

POST /roles

{
  "name": "Custom Role",
  "description": "...",
  "permissions": ["candidates:read", "jobs:read"]
}

PUT /roles/{id}

DELETE /roles/{id} — custom roles only

GET /roles/permissions

List all available permissions.

POST /roles/assign

{ "user_id": "uuid", "role_name": "hiring_manager" }

Candidates

GET /candidates

Query params: search, source, skill, page, page_size

POST /candidates

{
  "name": "John Doe",
  "email": "john@example.com",
  "current_title": "Senior Engineer",
  "years_exp": 6,
  "skills": ["Python", "React"],
  "location": "Hyderabad",
  "source": "linkedin"
}

GET /candidates/{id}

PUT /candidates/{id}

DELETE /candidates/{id} — Requires candidates:delete

GET /candidates/{id}/notes

POST /candidates/{id}/notes

{ "content": "Great candidate, strong Python background" }

Jobs

GET /jobs

Query params: status, search

POST /jobs

{
  "title": "Senior Backend Engineer",
  "department": "Engineering",
  "skills": ["Python", "FastAPI", "PostgreSQL"],
  "locations": ["Hyderabad", "Pune"],
  "min_exp": 4,
  "headcount": 2,
  "priority": "high"
}

GET /jobs/{id}

PUT /jobs/{id}

DELETE /jobs/{id}


Pipeline

GET /pipeline

Query params: job_req_id, stage

POST /pipeline

{
  "candidate_id": "uuid",
  "job_req_id": "uuid",
  "stage": "screening"
}

PUT /pipeline/{entry_id}

{
  "stage": "round_1",
  "notes": "Strong screening call"
}

Valid stages: sourced, screening, assessment, round_1, round_2, culture_fit, offer_negotiation, offer_extended, offer_accepted, joined, rejected, withdrawn

GET /pipeline/{entry_id}

Full entry with candidate, job, rounds, and offer.

POST /pipeline/rounds

{
  "pipeline_entry_id": "uuid",
  "round_number": 1,
  "round_type": "technical",
  "title": "System Design",
  "scheduled_at": "2025-01-15T10:00:00",
  "interviewer_id": "uuid"
}

PUT /pipeline/rounds/{round_id}

{
  "status": "completed",
  "rating": 4,
  "recommendation": "proceed",
  "feedback_text": "Strong candidate...",
  "strengths": "Architecture knowledge",
  "concerns": "Needs more React experience"
}

POST /pipeline/offers

{
  "pipeline_entry_id": "uuid",
  "offered_ctc": 24.0,
  "variable_component": 4.0,
  "joining_date": "2025-02-01",
  "offer_expiry": "2025-01-20"
}

PUT /pipeline/offers/{offer_id}

{ "status": "accepted" }

Sourcing

GET /sourcing/sheet-tabs?url=…

Returns tab names for a Google Sheets URL.

POST /sourcing/parse-sheet

{ "url": "https://docs.google.com/spreadsheets/d/...", "tab": "Q1 Roles" }

POST /sourcing/run

{
  "jds": [
    {
      "role": "Senior Backend Engineer",
      "skills": ["Python", "FastAPI"],
      "locations": ["Hyderabad"],
      "min_exp": 4
    }
  ]
}

Returns { "job_ids": ["uuid1", ...], "count": 1 }

GET /sourcing/jobs

GET /sourcing/jobs/{job_id}

GET /sourcing/jobs/{job_id}/stream?token=JWT

SSE stream of real-time log messages.

GET /sourcing/jobs/{job_id}/download

Downloads Excel file with scored candidates.


Analytics

GET /analytics/summary

{
  "open_jobs": 12,
  "total_candidates": 450,
  "active_pipeline": 67,
  "offers_extended": 4,
  "joined": 8
}

GET /analytics/funnel

Array of { stage, count } for all pipeline stages.

GET /analytics/source-quality

Array of { source, count } by candidate source.

GET /analytics/sourcing-jobs

Sourcing job stats by status.

GET /analytics/kpi-trends?period=weekly|daily

Time series for dashboard KPI sparklines. Permission: analytics:read.

{
  "period": "weekly",
  "pipeline_added": [{ "label": "W12 May", "cnt": 4 }],
  "hires": [{ "label": "W12 May", "cnt": 1 }]
}

GET /analytics/interviewer-stats?period=monthly|weekly

Permission: analytics:leaderboard. Cancelled interview rounds are excluded from totals and trends.

Returns { "leaderboard": [...], "trend": [...] }. Each leaderboard row: user_id, name, department, role, total_interviews (non-cancelled rounds), scheduled, completed.


AI Endpoints

All AI endpoints require ai:use permission.

POST /ai/score-candidate

// Request
{ "candidate_id": "uuid", "job_req_id": "uuid" }

// Response
{
  "score": 7.8,
  "reasoning": "Strong Python and FastAPI background with 6 years experience...",
  "strengths": ["FastAPI expertise", "System design", "Team player"],
  "gaps": ["No Kubernetes experience", "Below min experience by 1 year"]
}

Also persists ai_score, ai_reasoning, ai_strengths, ai_gaps to the candidate record.

POST /ai/parse-jd

// Request
{ "raw_text": "We are looking for a Senior Engineer with 5+ years..." }

// Response
{
  "title": "Senior Backend Engineer",
  "seniority": "senior",
  "skills": ["Python", "FastAPI", "PostgreSQL", "Docker"],
  "min_exp": 5,
  "must_haves": ["Python 3.10+", "REST API design"],
  "nice_to_haves": ["Kubernetes", "AWS"],
  "locations": ["Hyderabad", "Remote"],
  "department": "Engineering"
}

POST /ai/interview-questions

// Request
{ "job_req_id": "uuid", "candidate_id": "uuid", "round_type": "technical" }
// round_type: technical | behavioral | culture_fit | hr

// Response
{ "questions": ["Question 1...", "Question 2...", ...(10 total)] }

POST /ai/draft-offer-letter

// Request
{ "pipeline_entry_id": "uuid" }

// Response
{
  "subject": "Offer of Employment – Senior Backend Engineer at P99soft",
  "letter_text": "Dear John,\n\nWe are delighted to offer..."
}

Also saves letter_text to the offer record in the database.

POST /ai/recruiter-email

// Request
{
  "candidate_id": "uuid",
  "stage": "screening",
  "job_req_id": "uuid",
  "context": "Candidate has 7 years experience in fintech"
}

// Response
{
  "subject": "Interview Invitation – Senior Backend Engineer at P99soft",
  "body": "Hi John,\n\nI came across your profile and was impressed..."
}

POST /ai/enhance-queries

// Request
{ "job_req_id": "uuid" }

// Response
{
  "linkedin_queries": ["(Senior Engineer OR Lead Engineer) AND (Python OR FastAPI) ...", ...],
  "github_queries": ["language:python location:Hyderabad ..."],
  "naukri_queries": ["python fastapi senior engineer hyderabad", ...],
  "general_queries": ["site:github.com python developer Hyderabad", ...]
}

Health

GET /api/health

{ "status": "ok", "app": "P99soft Recruitment Platform", "version": "3.0.0", "env": "development" }

Error Responses

Code Meaning
400 Bad request / validation error
401 Missing or invalid/expired JWT
403 Insufficient permission or role
404 Resource not found (in your org)
409 Conflict (duplicate email, duplicate pipeline entry)
422 Pydantic validation error (request body)
429 Rate limit exceeded
500 Internal server error

All error responses follow:

{ "detail": "Error message here" }