v2 API Reference
Every v2 endpoint, grouped by module — matching the shipped Postman collection
Dev Onlyv1 endpoints (products, sales, inventory, customers, etc.) are documented in
the API Reference. This page covers the
/api/v2/* surface added in v2. Both are served by the same
backend. The authoritative, runnable source is the Postman collection in the
backend repo under /postman.
Connection & Conventions
| Property | Value |
|---|---|
| Base URL (dev) | http://localhost:5000 |
| v2 path prefix | /api/v2/... |
| Auth header | Authorization: Bearer <access_token> |
| Access / refresh token life | 15 minutes / 7 days (single-use refresh) |
| Socket.IO URL | http://localhost:5000 with auth: { token } |
| HR gating | All /hr/* and /me/* endpoints require the HR module enabled for the tenant. |
Response envelope
List endpoints accept ?page=&limit= (limit capped at 100), plus common
?search=, ?sortBy=, ?sortOrder=asc|desc, and date/status filters.
HR — Departments & Designations
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/hr/departments | hr.departments.read |
| POST | /api/v2/hr/departments | hr.departments.manage |
| GET | /api/v2/hr/departments/:id | hr.departments.read |
| PATCH | /api/v2/hr/departments/:id | hr.departments.manage |
| DELETE | /api/v2/hr/departments/:id | hr.departments.manage |
| PATCH | /api/v2/hr/departments/:id/restore | hr.departments.manage |
| GET | /api/v2/hr/designations | hr.designations.read |
| POST | /api/v2/hr/designations | hr.designations.manage |
| GET | /api/v2/hr/designations/:id | hr.designations.read |
| PATCH | /api/v2/hr/designations/:id | hr.designations.manage |
| DELETE | /api/v2/hr/designations/:id | hr.designations.manage |
| PATCH | /api/v2/hr/designations/:id/restore | hr.designations.manage |
HR — Employees
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/hr/employees | hr.employees.read |
| POST | /api/v2/hr/employees | hr.employees.create |
| GET | /api/v2/hr/employees/:id | hr.employees.read |
| PATCH | /api/v2/hr/employees/:id | hr.employees.update |
| DELETE | /api/v2/hr/employees/:id | hr.employees.update (soft delete) |
| PATCH | /api/v2/hr/employees/:id/restore | hr.employees.update |
| POST | /api/v2/hr/employees/:id/user | Link / create login user |
| PATCH | /api/v2/hr/employees/:id/sensitive | hr.employees.update.sensitive |
| PATCH | /api/v2/hr/employees/:id/salary | hr.employees.update.salary |
| POST | /api/v2/hr/employees/:id/terminate | hr.employees.terminate |
| GET | /api/v2/hr/employees/:id/contracts | hr.contracts.read |
| POST | /api/v2/hr/employees/:id/contracts | hr.contracts.create |
| GET | /api/v2/hr/employees/:id/documents | hr.employees.documents.read |
| POST | /api/v2/hr/employees/:id/documents | hr.employees.documents.upload |
| DELETE | /api/v2/hr/employees/:id/documents/:docId | hr.employees.documents.delete |
HR — Attendance
| Method | Path | Permission / Notes |
|---|---|---|
| POST | /api/v2/hr/attendance/check-in | self ess.attendance.check-in · others hr.attendance.check-in.manual |
| POST | /api/v2/hr/attendance/check-out | same as check-in |
| POST | /api/v2/hr/attendance/break-start | same as check-in |
| POST | /api/v2/hr/attendance/break-end | same as check-in |
| GET | /api/v2/hr/attendance/today | hr.attendance.read.team |
| GET | /api/v2/hr/attendance/summary | read (team/all) |
| GET | /api/v2/hr/attendance | hr.attendance.read.all |
| GET | /api/v2/hr/attendance/:id | attendance read |
| GET | /api/v2/hr/attendance/corrections | hr.attendance.regularize.approve |
| POST | /api/v2/hr/attendance/corrections | hr.attendance.regularize.request |
| GET | /api/v2/hr/attendance/corrections/:id | correction read |
| POST | /api/v2/hr/attendance/corrections/:id/approve | hr.attendance.regularize.approve |
| POST | /api/v2/hr/attendance/corrections/:id/reject | hr.attendance.regularize.reject |
| POST | /api/v2/hr/attendance/corrections/:id/cancel | owner only |
method: MANUAL · WEB · MOBILE_APP · GEOFENCE · IP_RESTRICTED · QR_CODE · BIOMETRIC.
Omit employeeId for self; supplying it forces MANUAL.
HR — Shifts
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/hr/shifts/templates | hr.shifts.read |
| GET | /api/v2/hr/shifts/templates/:id | hr.shifts.read |
| POST | /api/v2/hr/shifts/templates | hr.shifts.template.manage |
| PATCH | /api/v2/hr/shifts/templates/:id | hr.shifts.template.manage |
| DELETE | /api/v2/hr/shifts/templates/:id | hr.shifts.template.manage |
| POST | /api/v2/hr/shifts/templates/:id/restore | hr.shifts.template.manage |
| GET | /api/v2/hr/shifts/schedule | hr.shifts.schedule.read |
| POST | /api/v2/hr/shifts/schedule | hr.shifts.schedule.create (bulk 1–500) |
| PATCH | /api/v2/hr/shifts/schedule/:id | hr.shifts.schedule.update |
| DELETE | /api/v2/hr/shifts/schedule/:id | hr.shifts.schedule.delete |
| GET | /api/v2/hr/shifts/swap-requests | swap read |
| GET | /api/v2/hr/shifts/swap-requests/:id | swap read |
| POST | /api/v2/hr/shifts/swap-requests | hr.shifts.swap.request.own |
| POST | /api/v2/hr/shifts/swap-requests/:id/respond | hr.shifts.swap.respond |
| POST | /api/v2/hr/shifts/swap-requests/:id/approve | hr.shifts.swap.approve |
| POST | /api/v2/hr/shifts/swap-requests/:id/cancel | owner only |
HR — Leave
| Method | Path | Permission |
|---|---|---|
| GET / POST | /api/v2/hr/leave/types | hr.leave.types.read / .manage |
| GET / PATCH / DELETE | /api/v2/hr/leave/types/:id | hr.leave.types.read / .manage |
| POST | /api/v2/hr/leave/types/:id/restore | hr.leave.types.manage |
| GET / POST | /api/v2/hr/leave/policies | hr.leave.policies.read / .manage |
| PATCH | /api/v2/hr/leave/policies/:id | hr.leave.policies.manage |
| GET | /api/v2/hr/leave/balances | hr.leave.balances.read.all |
| POST | /api/v2/hr/leave/balances/adjust | hr.leave.balances.adjust |
| GET | /api/v2/hr/leave/requests | hr.leave.request.read.all |
| GET | /api/v2/hr/leave/requests/:id | request read |
| POST | /api/v2/hr/leave/requests | hr.leave.request.create.for |
| PATCH | /api/v2/hr/leave/requests/:id | request update (PENDING only) |
| POST | /api/v2/hr/leave/requests/:id/approve | hr.leave.request.approve |
| POST | /api/v2/hr/leave/requests/:id/reject | hr.leave.request.approve |
| POST | /api/v2/hr/leave/requests/:id/cancel | owner / approver |
HR — Holidays
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/hr/holidays/calendar?year=&storeId= | hr.holidays.read |
| POST | /api/v2/hr/holidays/import-preset | hr.holidays.manage (US/UK/IN/BD/UAE) |
| GET | /api/v2/hr/holidays | hr.holidays.read |
| POST | /api/v2/hr/holidays | hr.holidays.manage |
| GET | /api/v2/hr/holidays/:id | hr.holidays.read |
| PATCH | /api/v2/hr/holidays/:id | hr.holidays.manage |
| DELETE | /api/v2/hr/holidays/:id | hr.holidays.manage |
HR — Payroll
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/hr/payroll/country-presets | hr.payroll.country-preset.read |
| GET / POST | /api/v2/hr/payroll/salary-structures | hr.payroll.salary-structure.read / .manage |
| GET / PATCH / DELETE | /api/v2/hr/payroll/salary-structures/:id | ...salary-structure.read / .manage |
| POST | /api/v2/hr/payroll/salary-structures/:id/apply-preset | hr.payroll.country-preset.apply |
| POST / PATCH / DELETE | /api/v2/hr/payroll/salary-structures/:id/components[/:cid] | hr.payroll.salary-structure.manage |
| GET / POST | /api/v2/hr/payroll/employee-salaries | hr.payroll.salary-structure.manage |
| GET / POST | /api/v2/hr/payroll/runs | hr.payroll.read / .run.create |
| GET | /api/v2/hr/payroll/runs/:id | hr.payroll.read |
| POST | /api/v2/hr/payroll/runs/:id/process | hr.payroll.run.process |
| POST | /api/v2/hr/payroll/runs/:id/approve | hr.payroll.run.approve (SoD + step-up 2FA) |
| POST | /api/v2/hr/payroll/runs/:id/disburse | hr.payroll.run.disburse |
| POST | /api/v2/hr/payroll/runs/:id/cancel | hr.payroll.run.cancel |
| GET | /api/v2/hr/payroll/runs/:id/payslips | hr.payroll.read.payslip.all |
| GET | /api/v2/hr/payroll/payslips/:id | hr.payroll.read.payslip.all |
| GET | /api/v2/hr/payroll/payslips/:id/pdf | payslip read |
| POST | /api/v2/hr/payroll/payslips/:id/void | hr.payroll.payslip.void |
| GET / POST | /api/v2/hr/payroll/advances | hr.payroll.advance.read / .create |
| GET | /api/v2/hr/payroll/advances/:id | hr.payroll.advance.read |
| POST | /api/v2/hr/payroll/advances/:id/approve | hr.payroll.advance.approve |
| POST | /api/v2/hr/payroll/advances/:id/reject | hr.payroll.advance.approve |
| POST | /api/v2/hr/payroll/advances/:id/disburse | hr.payroll.advance.disburse |
| DELETE | /api/v2/hr/payroll/advances/:id | cancel (pre-disbursement) |
Employee Self-Service (ESS) — /api/v2/me/*
All endpoints self-scope to the caller's linked employee. Client-supplied employee ids are ignored.
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/me/dashboard | ess (linked employee) |
| GET / PATCH | /api/v2/me/profile | ess.profile.read / .update |
| GET | /api/v2/me/attendance | ess.attendance.read |
| GET | /api/v2/me/attendance/today | ess.attendance.read |
| GET | /api/v2/me/attendance/summary | ess.attendance.read |
| POST | /api/v2/me/attendance/check-in | ess.attendance.check-in |
| POST | /api/v2/me/attendance/check-out | ess.attendance.check-out |
| POST | /api/v2/me/attendance/break-start | ess.attendance.check-in |
| POST | /api/v2/me/attendance/break-end | ess.attendance.check-out |
| POST | /api/v2/me/attendance/regularize | ess.attendance.regularize |
| GET | /api/v2/me/shifts | ess.shifts.read |
| POST | /api/v2/me/shifts/swap-request | ess.shifts.swap.request |
| POST | /api/v2/me/shifts/swap-respond/:id | ess.shifts.swap.respond |
| GET | /api/v2/me/leave/types | ess.leave.balance.read |
| GET | /api/v2/me/leave/balance | ess.leave.balance.read |
| GET | /api/v2/me/leave/requests | ess.leave.request.read |
| POST | /api/v2/me/leave/requests | ess.leave.request.create |
| POST | /api/v2/me/leave/requests/:id/cancel | ess.leave.request.read (own) |
| GET | /api/v2/me/payslips | ess.payslips.read |
| GET | /api/v2/me/payslips/:id | ess.payslips.read |
| GET | /api/v2/me/payslips/:id/pdf | ess.payslips.download |
| GET | /api/v2/me/documents | ess.documents.read |
| GET | /api/v2/me/holidays?year= | ess.holidays.read |
Catalog — Brands
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/brands | brands.read |
| POST | /api/v2/brands | brands.manage |
| GET | /api/v2/brands/:id | brands.read |
| PATCH | /api/v2/brands/:id | brands.manage |
| DELETE | /api/v2/brands/:id | brands.manage (soft delete) |
| PATCH | /api/v2/brands/:id/restore | brands.manage |
Stores — Attendance Geofencing
| Method | Path | Permission | Body |
|---|---|---|---|
| PATCH | /api/v2/stores/:id/geolocation | stores.geolocation.update | { geoLat, geoLng, geoRadiusM } (1–50000, or all null to clear) |
| PATCH | /api/v2/stores/:id/ip-whitelist | stores.ip-whitelist.update | Array of CIDR strings |
Reports — Dashboard & HR Analytics
| Method | Path | Consumer |
|---|---|---|
| GET | /api/v2/reports/dashboard | KPI rollup (reserved for the consolidated dashboard) |
| GET | /api/v2/reports/employees | /reports/hr → Employees tab |
| GET | /api/v2/reports/attendance | /reports/hr → Attendance tab |
| GET | /api/v2/reports/leave | /reports/hr → Leave tab |
| GET | /api/v2/reports/payroll | /reports/hr → Payroll tab |
| GET | /api/v2/reports/activity | /reports/hr → Activity tab |
Notifications — Real-Time
| Method | Path | Permission |
|---|---|---|
| GET | /api/v2/notifications | Any authenticated (own rows) |
| GET | /api/v2/notifications/unread-count | Any authenticated |
| POST | /api/v2/notifications/read-all | Any authenticated |
| PATCH | /api/v2/notifications/:id/read | Any authenticated (own) |
| POST | /api/v2/notifications/broadcast | notifications.send.manual |
Socket.IO Rooms & Events
| Room | Used for |
|---|---|
tenant:{tid}:user:{uid} | Per-user notification delivery |
tenant:{tid}:role:{role} | Role-addressed delivery |
tenant:{tid} | Tenant-wide |
tenant:{tid}:store:{sid} | Per-store (scanner relay, stock, store alerts) |
| Event | Direction | Payload |
|---|---|---|
notification:new | server → user room | the persisted notification row |
scanner:scan | client → server | { barcode } |
scanner:relay | server → POS clients | { barcode, scannerDeviceId } |
stock:updated | server → store room | { productId, storeId, newQty } |
Error Codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Body failed schema validation — see error.details. |
| 401 | AUTHENTICATION_FAILED | Missing/expired access token. |
| 403 | AUTHORIZATION_FAILED | Valid token, but missing the required permission. |
| 403 | EMPLOYMENT_INACTIVE | ESS write attempted by a terminated/resigned employee. |
| 404 | NOT_FOUND | Resource missing or not yours. |
| 409 | CONFLICT | Unique constraint / state conflict (e.g. duplicate employeeCode). |
| 409 | NO_LINKED_EMPLOYEE | ESS called by a login with no employee record. |
| 409 | INSUFFICIENT_BALANCE | Leave request exceeds available balance. |
| 403 | MODULE_DISABLED | HR module not enabled for this tenant. |
Import postman/POSVelo-API.postman_collection.json and
postman/POSVelo-Development.postman_environment.json, log in, and
every request above is one click away — with example bodies and auto-chained
IDs. See What's New in v2 for the quick start.