# HRMS SaaS API Documentation

Base URL: `/api`

## Authentication

### Login
```http
POST /api/auth/login
Content-Type: application/json

{
  "email": "user@company.com",
  "password": "secret"
}
```

Login is email + password. If that pair matches one account, you are signed in. If it matches more than one account (same email and password in multiple companies), the API returns an error asking you to contact an administrator. Optional `company` (tenant `short_name`) may still be sent to narrow the lookup.
Response (no 2FA): `{ "user": {...}, "token": "...", "token_type": "Bearer" }`

When two-factor authentication applies (user or company policy), the login response is:

```json
{
  "requires_two_factor": true,
  "challenge_token": "...",
  "method": "email",
  "masked_email": "u•••@company.com"
}
```

Complete login:

```http
POST /api/auth/two-factor/verify
Content-Type: application/json

{
  "challenge_token": "...",
  "code": "123456"
}
```

Resend email code:

```http
POST /api/auth/two-factor/resend
Content-Type: application/json

{ "challenge_token": "..." }
```

If TOTP enrollment is required before first API login, login returns `403` with `error: two_factor_enrollment_required` — complete setup in the web app first.

### Forgot password
```http
POST /api/auth/forgot-password
Content-Type: application/json

{ "company": "demo", "email": "user@company.com" }
```
Sends the same reset email as the web app (if the account exists and login is allowed). Always returns a generic success message.

### Reset password
```http
POST /api/auth/reset-password
Content-Type: application/json

{
  "email": "user@company.com",
  "token": "token-from-email",
  "password": "new-secret",
  "password_confirmation": "new-secret"
}
```
Revokes all API tokens on success.

### Logout
```http
POST /api/auth/logout
Authorization: Bearer {token}
```

### Get current user
```http
GET /api/auth/user
Authorization: Bearer {token}
```

### Register (use company registration flow)
```http
POST /api/auth/register
```
Returns 422 with message to use company registration endpoint.

---

## Employees
All employee endpoints require `Authorization: Bearer {token}` and tenant context.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/employees | List employees (paginated) |
| POST | /api/employees | Create employee |
| GET | /api/employees/{id} | Get employee |
| PUT/PATCH | /api/employees/{id} | Update employee |
| DELETE | /api/employees/{id} | Delete employee |

---

## Attendance
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/attendance/today | Today's attendance status |
| POST | /api/attendance/clock-in | Clock in (optional: latitude, longitude) |
| POST | /api/attendance/clock-out | Clock out |
| GET | /api/attendance | List attendances (query: from, to, per_page) |
| GET | /api/attendance/{id} | Get attendance |

---

## Leaves
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/leaves | List leaves |
| POST | /api/leaves | Apply leave (leave_type_id, start_date, end_date, reason, is_half_day, half_leave) |
| GET | /api/leaves/{id} | Get leave |
| PUT/PATCH | /api/leaves/{id} | Update pending leave |
| DELETE | /api/leaves/{id} | Delete pending leave |

---

## Locations
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/locations | List company locations (paginated) |
| POST | /api/locations | Create location (`name`, `code`, `address`) |
| GET | /api/locations/{id} | Get location |
| PUT/PATCH | /api/locations/{id} | Update location |
| DELETE | /api/locations/{id} | Delete location (blocked if assigned to employees) |

---

## Payroll
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/payroll | List payrolls (query: year, month, per_page) |
| GET | /api/payroll/my-payslip | Current user payslip (query: year, month) |
| GET | /api/payroll/{id} | Get payroll |
| GET | /api/payroll/cycles/{cycle}/bank-export | Download NEFT/bank CSV for a payroll cycle |

---

## Notifications
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/notifications/feed | Paginated in-app notification feed |
| POST | /api/notifications/{id}/read | Mark one notification read |
| POST | /api/notifications/mark-all-read | Mark all read |

---

## Expenses
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/expenses | List expenses (query: `scope=self\|team\|company`, `status`, `per_page`) |
| POST | /api/expenses | Submit expense claim |
| GET | /api/expenses/{id} | Get expense |
| POST | /api/expenses/{id}/status | Approve/reject/paid (team or finance approvers) |

**Scope** (for `GET /api/expenses`):
- `self` — own claims (staff default)
- `team` — direct reports' claims (managers with `finance_team_view`)
- `company` — all company claims (HR / finance roles)

---

## Finance (self-service)
Role-aware finance endpoints. Call `GET /api/finance/home` first for zone, tabs, and expense scope keys.

| Method | Endpoint | Permission |
|--------|----------|------------|
| GET | /api/finance/home | Any finance module permission |
| GET | /api/finance/self | `finance_self_view` or `expenses_create_own` |
| GET | /api/finance/team | `finance_team_view` |
| GET | /api/finance/operational | `finance_operational` or treasury |
| GET | /api/finance/my-advances | `finance_self_view` or `expenses_create_own` |

### GET /api/finance/home
```json
{
  "zone": "My Finance",
  "home_path": "/finance/self",
  "expense_scopes": [{ "key": "self", "label": "My claims" }],
  "tabs": [{ "key": "self", "label": "My Finance", "url": "https://..." }]
}
```

### GET /api/finance/self
```json
{
  "pending_claims": 2,
  "pending_claims_amount": 1500.0,
  "approved_mtd": 800.0,
  "paid_mtd": 0.0,
  "outstanding_advance": 12000.0,
  "next_deduction": {
    "id": 42,
    "amount": 2000.0,
    "month": 7,
    "year": 2026,
    "advance_date": "2026-06-01",
    "installment_label": "2 / 6"
  },
  "recent_claims": [
    {
      "id": 10,
      "category": "Travel",
      "amount": 500.0,
      "date": "2026-06-15",
      "status": "pending",
      "description": null
    }
  ]
}
```

### GET /api/finance/team
Manager team approval summary (`finance_team_view`).

### GET /api/finance/operational
HR / treasury operational queue (`finance_operational` or treasury permissions).

### GET /api/finance/my-advances
```json
{
  "summary": {
    "outstanding": 12000.0,
    "pending_installments": 6,
    "next_deduction": { "id": 42, "amount": 2000.0, "month": 7, "year": 2026 }
  },
  "plans": [
    {
      "group_id": "uuid-or-null",
      "advance_date": "2026-06-01",
      "total_amount": 12000.0,
      "remaining": 10000.0,
      "installments": 6,
      "pending_of": 5
    }
  ],
  "advances": {
    "data": [
      {
        "id": 42,
        "advance_date": "2026-06-01",
        "deduction_month": 7,
        "deduction_year": 2026,
        "amount": 2000.0,
        "installment_label": "2 / 6",
        "status": "pending"
      }
    ],
    "current_page": 1,
    "last_page": 1,
    "per_page": 20,
    "total": 6
  }
}
```

## Recruitment (mobile)

Call `GET /api/recruitment/home` first for zone and tab URLs.

| Method | Path | Permission |
|--------|------|------------|
| GET | /api/recruitment/home | `recruitment_view`, `recruitment_refer`, or `recruitment_referrals_view` |
| GET | /api/recruitment/open-roles | `recruitment_refer` or `recruitment_view` |
| GET | /api/recruitment/my-referrals | `recruitment_referrals_view` or `recruitment_refer` |
| GET | /api/recruitment/my-referrals/{id} | own referral only |
| POST | /api/recruitment/refer | `recruitment_refer` (multipart: resume optional) |
| GET | /api/recruitment/pipeline | `recruitment_view` (kanban columns) |
| GET | /api/recruitment/referral-leaderboard | `recruitment_view` |

### GET /api/recruitment/home
```json
{
  "zone": "staff",
  "home_path": "/recruitment/my-referrals",
  "can_refer": true,
  "can_track_referrals": true,
  "has_hr_access": false,
  "tabs": [
    { "key": "my_referrals", "label": "My referrals", "url": "/recruitment/my-referrals" }
  ]
}
```

### GET /api/recruitment/my-referrals
Query: `status` (`active`|`closed`|`all`), `per_page`.

### POST /api/recruitment/refer
Body: `job_posting_id`, `name`, `email`, `phone`, `notes`, optional `resume` file. Returns `422` if duplicate email for the same active job.

## Public careers (web, no auth)

| Method | Path | Description |
|--------|------|-------------|
| GET | /careers/{companyKey} | List active jobs (`companyKey` = company id or short_name) |
| GET | /careers/{companyKey}/jobs/{id} | Application form |
| POST | /careers/{companyKey}/apply | Submit external application |

SLA: new applicants get `sla_due_at` = applied date + `RECRUITMENT_SCREENING_SLA_DAYS` (default 5). HR dashboard surfaces overdue items.

---

## Forms
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/forms | Published forms |
| GET | /api/forms/{id} | Form fields |
| POST | /api/forms/{id}/submit | Submit (`responses.field_0`, …) |
| GET | /api/forms/my-submissions | Current user's submissions |
| GET | /api/form-submissions | HR list (documents_view) |
| GET | /api/form-submissions/{id} | HR view submission |

---

## HR mobile (Phase 6+)
| Method | Endpoint | Permission |
|--------|----------|------------|
| GET | /api/hr/resignations | Self or HR list |
| GET | /api/hr/resignations/{id} | View resignation |
| GET | /api/hr/offboardings | Offboarding list |
| GET | /api/hr/offboardings/{id} | Offboarding detail + checklist |
| GET | /api/hr/performance-reviews | Performance reviews |
| GET | /api/hr/performance-cycles | Performance cycles |
| GET | /api/hr/deposits | Finance deposits (`finance_treasury_view`) |
| GET | /api/hr/generated-letters | Generated letters |
| GET | /api/hr/generated-letters/{id} | Letter detail |
| GET | /api/hr/pre-payments | Salary advances list (`finance_operational` or treasury) |
| POST | /api/hr/pre-payments | Record advance (`finance_create` + operational/treasury) |

---

## Leaves (approvers)
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/leaves/{leave}/status | Approve/reject leave |

---

## Subscription (tenant + subscription middleware)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/subscription/status | Company subscription status |

---

## Multi-tenancy
- All tenant-scoped endpoints automatically filter by `company_id` from the authenticated user.
- Super admin users (`is_superadmin = 1`) bypass company scope for platform management.
- Use header `Authorization: Bearer {token}` for API requests.

## Rate limiting
API routes are throttled (configurable in `App\Http\Middleware` or route middleware).
