# EduAdvise REST API Reference

Base URL: `https://your-domain.com/api/v1`

All responses follow the shape:
```json
{ "data": <T> | null, "error": string | null, "meta": { "page", "limit", "total", "pages" } }
```

---

## Authentication

The API uses **Bearer JWT tokens** issued by Supabase Auth. Obtain a token via the login endpoint, then pass it as:
```
Authorization: Bearer <access_token>
```

The same Row-Level Security (RLS) policies that protect the web UI apply to all API requests — a user can only see and modify data from their assigned branches.

---

## Auth

### POST /api/v1/auth/login
Obtain a JWT access token.

**Body**
```json
{ "email": "user@example.com", "password": "secret" }
```

**Response 200**
```json
{
  "data": {
    "access_token": "eyJ...",
    "refresh_token": "...",
    "expires_in": 3600,
    "user": { "id": "uuid", "email": "user@example.com" }
  },
  "error": null
}
```

**Response 401** — invalid credentials
**Response 400** — missing email or password

---

### POST /api/v1/auth/logout
Invalidate the current token. Requires Bearer token.

**Response 204** — success

---

### GET /api/v1/auth/me
Returns the current user's profile. Requires Bearer token.

**Response 200**
```json
{
  "data": {
    "id": "uuid",
    "full_name": "Jane Doe",
    "email": "jane@example.com",
    "phone": "+91 9876543210",
    "role_slug": "counselor",
    "primary_branch_id": "uuid",
    "branchIds": ["uuid"],
    "isSuperAdmin": false
  },
  "error": null
}
```

---

## Health Check

### GET /api/v1/healthz
No auth required. Returns DB connectivity status.

**Response 200**
```json
{ "data": { "status": "ok", "db": "ok", "timestamp": "2026-06-05T10:00:00Z" }, "error": null }
```
**Response 503** — DB unreachable

---

## Leads

### GET /api/v1/leads
List leads with filters and pagination.

| Param | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Per page, max 100 (default: 20) |
| status | string | Filter by lead_status (New, Contacted, Interested, Follow-up, Converted, Lost) |
| branch_id | uuid | Filter by branch |
| search | string | Search full_name, email, phone |

**Response 200**
```json
{
  "data": [{ "id": "...", "full_name": "John", "lead_status": "New", ... }],
  "error": null,
  "meta": { "page": 1, "limit": 20, "total": 150, "pages": 8 }
}
```

---

### POST /api/v1/leads
Create a lead.

**Required:** `full_name`
**Optional:** `branch_id` (defaults to user's branch), `email`, `phone`, `lead_status`, `lead_source_id`, `preferred_country`, `comments`, `remarks`, `gender`, `date_of_birth`, `country`, `city`

**Response 201** — created lead object

---

### GET /api/v1/leads/:id
Single lead with assignments and lead source.

**Response 404** if not found.

---

### PATCH /api/v1/leads/:id
Partial update. Only send the fields you want to change.

---

### DELETE /api/v1/leads/:id
**Response 204**

---

### GET /api/v1/leads/:id/followups
List followups for a lead, newest first.

---

### POST /api/v1/leads/:id/followups
Create a followup.

**Body:** `next_date`, `from_time`, `to_time`, `followup_type_id`, `status` (pending/done/missed), `notes`, `branch_id`

---

### GET /api/v1/leads/:id/assignments
List counselor assignments.

---

### POST /api/v1/leads/:id/assignments
Assign a counselor.

**Body:** `{ "user_id": "uuid" }`

---

## Student Applications

### GET /api/v1/applications/student
| Param | Description |
|---|---|
| page, limit | Pagination |
| status | Filter by status (Applied, Offer Received, Visa Applied, Visa Approved, Visa Rejected, Travelled) |
| branch_id | Filter by branch |
| search | applicant_name, email |

### POST /api/v1/applications/student
**Required:** `applicant_name`
**Optional:** `branch_id`, `lead_id`, `email`, `phone`, `country`, `institute_id`, `campus_id`, `course_id`, `intake_month`, `intake_year`, `application_type_id`, `status` (default: Applied), `counselor_id`, `tuition_fee`, `currency`, `remarks`

### GET /api/v1/applications/student/:id
With joined institute, campus, course, counselor, lead.

### PATCH /api/v1/applications/student/:id
Updatable: `status`, `counselor_id`, `institute_id`, `campus_id`, `course_id`, `intake_month`, `intake_year`, `offer_received`, `offer_date`, `tuition_fee`, `currency`, `remarks`

### DELETE /api/v1/applications/student/:id
**Response 204**

---

## Coaching Applications

### GET /api/v1/applications/coaching
Params: `page`, `limit`, `status`, `branch_id`

### POST /api/v1/applications/coaching
**Required:** `student_name`
**Optional:** `branch_id`, `lead_id`, `email`, `phone`, `subject_id`, `level_id`, `faculty_id`, `register_for_id`, `status` (default: Enrolled), `fees`, `start_date`

### GET | PATCH | DELETE /api/v1/applications/coaching/:id

---

## Fee Payments

### GET /api/v1/fee-payments
Params: `page`, `limit`, `branch_id`, `from_date`, `to_date`, `student_application_id`

### POST /api/v1/fee-payments
**Required:** `amount` (> 0)
**Optional:** `branch_id`, `student_application_id`, `currency` (default: INR), `payment_date` (default: today), `mode`, `reference`, `plan_sub_id`, `notes`

### GET /api/v1/fee-payments/:id
### DELETE /api/v1/fee-payments/:id
Fee payments are not editable — delete and recreate.

---

## Education Loan Inquiries

### GET /api/v1/loan-inquiry
Params: `page`, `limit`, `branch_id`, `status_id`, `search`

### POST /api/v1/loan-inquiry
**Required:** `applicant_name`
**Optional:** `branch_id`, `lead_id`, `bank_name`, `amount`, `currency`, `status_id`, `sanctioned_date`, `notes`

### GET | PATCH | DELETE /api/v1/loan-inquiry/:id
PATCH fields: `bank_name`, `amount`, `currency`, `status_id`, `sanctioned_date`, `notes`

---

## Courses

### GET /api/v1/courses
Params: `page`, `limit`, `country`, `institute_id`, `program_level_id`, `stream_id`, `search`
Always filters `is_active = true`.

### POST /api/v1/courses
**Required:** `title`
**Optional:** `institute_id`, `campus_id`, `country`, `state`, `program_level_id`, `qualification_id`, `stream_id`, `application_fee`, `yearly_tuition_fee`, `currency`, `duration_months`, `duration_label`, `intake_months`, `intake_year`, `requirements`, `tags`

### GET | PATCH | DELETE /api/v1/courses/:id

---

## Users

*Requires super-admin or branch-manager role for list/create.*

### GET /api/v1/users
Params: `page`, `limit`, `branch_id`, `role_id`, `search`

### POST /api/v1/users
Creates a **profile row only** (`status: "invited"`). Use the web admin UI to create the auth account.

**Required:** `full_name`, `email`

### GET /api/v1/users/:id
### PATCH /api/v1/users/:id
Super-admin or self only. Fields: `full_name`, `phone`, `role_id`, `primary_branch_id`, `status`, `b2b_country`, `b2b_state`, `joining_date`, `date_of_birth`

### DELETE /api/v1/users/:id
Super-admin only. Soft-deletes (sets `status = "suspended"`).

---

## Masters (Lookup Tables)

Generic CRUD for all lookup/reference tables.

Valid resource names:
`inquiry-types`, `exams`, `degrees`, `followup-types`, `lead-sources`, `sources-of-reference`, `tags`, `countries`, `states`, `institutes`, `campuses`, `program-levels`, `qualifications`, `streams`, `roles`, `branches`, `application-types`, `account-statuses`, `education-loan-statuses`, `visa-statuses`, `expense-types`, `coaching-subjects`, `coaching-levels`, `coaching-faculty`

### GET /api/v1/masters/:resource
Params: `page`, `limit`

### POST /api/v1/masters/:resource
Requires super-admin or branch-manager. Body: `name` (required), `is_active` (default true), other table-specific fields.

### GET | PATCH | DELETE /api/v1/masters/:resource/:id
DELETE is super-admin only (hard delete).

---

## Reports

All report endpoints support: `from_date`, `to_date`, `branch_id`

### GET /api/v1/reports/leads
Additional param: `status`

**Response:**
```json
{
  "data": {
    "total": 245,
    "by_status": { "New": 80, "Converted": 45, "Lost": 30 },
    "rows": [...]
  }
}
```

### GET /api/v1/reports/finance
**Response:**
```json
{
  "data": {
    "total_amount": 1250000,
    "count": 48,
    "by_currency": { "INR": 1100000, "GBP": 150000 },
    "rows": [...]
  }
}
```

### GET /api/v1/reports/applications
Additional param: `status`

**Response:**
```json
{
  "data": {
    "total": 180,
    "by_status": { "Applied": 60, "Offer Received": 45, "Visa Approved": 30 },
    "rows": [...]
  }
}
```

---

## Error Responses

| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid body fields |
| 401 | Unauthorized — missing or invalid Bearer token |
| 403 | Forbidden — authenticated but lacks permission |
| 404 | Resource not found |
| 405 | Method not allowed |
| 500 | Server error |
| 503 | Service unavailable (DB unreachable) |

Error response shape:
```json
{ "data": null, "error": "Human-readable error message" }
```

---

## Quick Start (cURL)

```bash
# 1. Get a token
TOKEN=$(curl -s -X POST https://your-domain.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@eduadvise.in","password":"your-password"}' \
  | jq -r '.data.access_token')

# 2. List leads
curl -H "Authorization: Bearer $TOKEN" \
  "https://your-domain.com/api/v1/leads?page=1&limit=10&status=New"

# 3. Create a lead
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"full_name":"Raj Sharma","phone":"+91 9876543210","preferred_country":"UK"}' \
  "https://your-domain.com/api/v1/leads"

# 4. Record a fee payment
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount":25000,"mode":"UPI","reference":"TXN123"}' \
  "https://your-domain.com/api/v1/fee-payments"
```
