API Reference
Both HQ and Store servers expose REST APIs under the /api/v1/ prefix. All responses use the standard envelope:
json
{
"success": true,
"data": { ... },
"error": "string (only on failure)"
}Paginated endpoints add total, page, and pageSize to the response.
Authentication
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/auth/login | No | Login with username + PIN/password |
| POST | /api/v1/auth/pin-login | No | Login with PIN only (POS/store) |
| POST | /api/v1/auth/refresh | No | Refresh access token |
| GET | /api/v1/auth/me | JWT | Get current user info + permissions |
| POST | /api/v1/auth/change-credential | JWT | Change PIN/password |
Login Request
json
{ "username": "admin", "pin": "1234" }Login Response
json
{
"success": true,
"data": {
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"user": { "id": "uuid", "username": "admin", "displayName": "Administrator", "isActive": true },
"permissions": ["products.view", "products.create", ...]
}
}Products
All routes require JWT authentication.
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/products | products.view | List products (search, departmentId, page, pageSize) |
| GET | /api/v1/products/:id | products.view | Get single product with tax rates |
| POST | /api/v1/products | products.create | Create product |
| PUT | /api/v1/products/:id | products.edit | Update product |
| PATCH | /api/v1/products/:id/toggle-active | products.edit | Toggle active status |
| DELETE | /api/v1/products/:id | products.delete | Soft delete (sets isActive=false) |
| GET | /api/v1/products/barcode/:barcode | products.view | Look up product by barcode |
| GET | /api/v1/products/:productId/suppliers | products.view | List suppliers for a product |
| POST | /api/v1/products/:productId/suppliers | products.edit | Link supplier to product |
| PUT | /api/v1/products/:productId/suppliers/:supplierId | products.edit | Update product-supplier link |
| DELETE | /api/v1/products/:productId/suppliers/:supplierId | products.edit | Unlink supplier from product |
Create/Update Product Body
json
{
"sku": "PROD-001",
"barcode": "1234567890123",
"name": "Product Name",
"description": "Optional description",
"departmentId": "uuid or null",
"costPrice": 500,
"sellPrice": 999,
"taxGroupId": "uuid or null",
"trackStock": true
}Departments
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/departments | products.view | List all departments |
| POST | /api/v1/departments | products.create | Create department |
| PUT | /api/v1/departments/:id | products.edit | Update department |
| DELETE | /api/v1/departments/:id | products.delete | Delete department |
Taxes
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/taxes | taxes.view | List tax rates |
| POST | /api/v1/taxes | taxes.manage | Create tax rate |
| PUT | /api/v1/taxes/:id | taxes.manage | Update tax rate |
| DELETE | /api/v1/taxes/:id | taxes.manage | Delete tax rate |
| GET | /api/v1/taxes/groups | taxes.view | List tax groups |
| GET | /api/v1/taxes/groups/:id | taxes.view | Get single tax group with rates |
| POST | /api/v1/taxes/groups | taxes.manage | Create tax group |
| PUT | /api/v1/taxes/groups/:id | taxes.manage | Update tax group |
| DELETE | /api/v1/taxes/groups/:id | taxes.manage | Delete tax group |
Suppliers
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/suppliers | suppliers.view | List suppliers |
| POST | /api/v1/suppliers | suppliers.create | Create supplier |
| PUT | /api/v1/suppliers/:id | suppliers.edit | Update supplier |
| DELETE | /api/v1/suppliers/:id | suppliers.delete | Delete supplier |
Users
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/users | users.view | List users |
| POST | /api/v1/users | users.create | Create user |
| PUT | /api/v1/users/:id | users.edit | Update user |
| GET | /api/v1/users/store-employees | JWT | List employees for current store |
Roles
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/roles | roles.view | List roles |
| POST | /api/v1/roles | roles.manage | Create role |
| PUT | /api/v1/roles/:id | roles.manage | Update role |
| GET | /api/v1/roles/permissions/all | roles.view | List all available permissions |
| PUT | /api/v1/roles/:id/permissions | roles.manage | Update role's permission set |
Stores
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/stores | stores.view | List stores |
| GET | /api/v1/stores/current | JWT | Get current store info (store mode) |
| GET | /api/v1/stores/dashboard | stores.view | Store dashboard with sales, sync, registers |
| POST | /api/v1/stores | stores.create | Create store |
| PUT | /api/v1/stores/:id | stores.edit | Update store |
Sales
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/sales | pos.sell | List sales (date_from, date_to, store_id, status, page) |
| POST | /api/v1/sales | pos.sell | Create a complete sale with items and payments |
| PUT | /api/v1/sales/:id/void | pos.void | Void a sale |
| POST | /api/v1/sales/print | pos.sell | Generate receipt data |
Create Sale Body
json
{
"receiptNumber": "R-0001",
"cashierId": "uuid",
"terminalId": "T01",
"subtotal": 1998,
"taxTotal": 165,
"discountTotal": 0,
"grandTotal": 2163,
"storeId": "uuid",
"salesRepId": "uuid or null",
"customerId": "uuid or null",
"items": [
{
"productId": "uuid",
"productName": "Coffee",
"quantity": 2000,
"unitPrice": 999,
"taxRate": 825,
"taxAmount": 165,
"discountAmount": 0,
"lineTotal": 2163,
"taxes": [
{ "taxRateName": "State Tax", "rate": 825, "taxAmount": 165, "isCompound": false, "sortOrder": 0 }
]
}
],
"payments": [
{ "method": "CASH", "amount": 2200, "reference": null }
]
}Inventory
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/inventory | inventory.view | List inventory levels (store_id, product_id, low_stock, page) |
| POST | /api/v1/inventory/adjustments | inventory.adjust | Create inventory adjustment |
| GET | /api/v1/inventory/cross-store | inventory.view | Cross-store inventory view |
Transfers
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/transfers | transfers.view | List transfers |
| GET | /api/v1/transfers/:id | transfers.view | Get single transfer with items |
| POST | /api/v1/transfers | transfers.create | Create transfer |
| PUT | /api/v1/transfers/:id/send | transfers.send | Mark transfer as sent |
| PUT | /api/v1/transfers/:id/receive | transfers.receive | Receive transfer |
| PUT | /api/v1/transfers/:id/cancel | transfers.create | Cancel transfer |
Registers
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/registers | registers.view | List register sessions |
| GET | /api/v1/registers/current | JWT | Get current open register session |
| POST | /api/v1/registers/open | registers.open | Open register |
| POST | /api/v1/registers/close | registers.close | Close register |
| PUT | /api/v1/registers/:id/close | registers.close | Close specific session |
Tenders
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/tenders | tenders.view | List payment methods |
| POST | /api/v1/tenders | tenders.manage | Create tender type |
| PUT | /api/v1/tenders/:id | tenders.manage | Update tender type |
| DELETE | /api/v1/tenders/:id | tenders.manage | Delete tender type |
Customers
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/customers | customers.view | List customers |
| GET | /api/v1/customers/:id | customers.view | Get single customer |
| POST | /api/v1/customers | customers.create | Create customer |
| PUT | /api/v1/customers/:id | customers.edit | Update customer |
| DELETE | /api/v1/customers/:id | customers.edit | Delete customer |
Sales Reps
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/sales-reps | sales_reps.view | List sales representatives |
| GET | /api/v1/sales-reps/:id | sales_reps.view | Get single sales rep |
| POST | /api/v1/sales-reps | sales_reps.create | Create sales rep |
| PUT | /api/v1/sales-reps/:id | sales_reps.edit | Update sales rep |
| PATCH | /api/v1/sales-reps/:id/toggle-active | sales_reps.edit | Toggle active status |
| DELETE | /api/v1/sales-reps/:id | sales_reps.delete | Delete sales rep |
Store Prices
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/store-prices | store_prices.view | List store price overrides |
| POST | /api/v1/store-prices | store_prices.manage | Upsert store price override |
| DELETE | /api/v1/store-prices/:id | store_prices.manage | Delete store price override |
Purchase Orders
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/purchase-orders | purchase_orders.view | List purchase orders |
| GET | /api/v1/purchase-orders/:id | purchase_orders.view | Get PO with items |
| POST | /api/v1/purchase-orders | purchase_orders.create | Create PO |
| PUT | /api/v1/purchase-orders/:id | purchase_orders.edit | Update PO |
| PATCH | /api/v1/purchase-orders/:id/submit | purchase_orders.create | Submit PO for approval |
| PATCH | /api/v1/purchase-orders/:id/approve | purchase_orders.approve | Approve PO |
| PATCH | /api/v1/purchase-orders/:id/send | purchase_orders.edit | Mark PO as sent |
| PATCH | /api/v1/purchase-orders/:id/receive | purchase_orders.receive | Receive PO items |
| PATCH | /api/v1/purchase-orders/:id/cancel | purchase_orders.edit | Cancel PO |
| DELETE | /api/v1/purchase-orders/:id | purchase_orders.delete | Delete draft PO |
| POST | /api/v1/purchase-orders/:id/items | purchase_orders.edit | Add item to PO |
| PUT | /api/v1/purchase-orders/:id/items/:itemId | purchase_orders.edit | Update PO item |
| DELETE | /api/v1/purchase-orders/:id/items/:itemId | purchase_orders.edit | Remove PO item |
Specials
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/specials | specials.view | List all specials |
| GET | /api/v1/specials/active | specials.view | List currently active specials |
| POST | /api/v1/specials | specials.manage | Create special |
| PUT | /api/v1/specials/:id | specials.manage | Update special |
| DELETE | /api/v1/specials/:id | specials.manage | Delete special |
Worksheets
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/worksheets | worksheets.view | List worksheets |
| GET | /api/v1/worksheets/:id | worksheets.view | Get worksheet with items and stores |
| POST | /api/v1/worksheets | worksheets.create | Create worksheet |
| PUT | /api/v1/worksheets/:id | worksheets.create | Update draft worksheet |
| DELETE | /api/v1/worksheets/:id | worksheets.delete | Delete draft worksheet |
| PATCH | /api/v1/worksheets/:id/submit | worksheets.submit | Submit for approval |
| PATCH | /api/v1/worksheets/:id/approve | worksheets.approve | Approve worksheet |
| PATCH | /api/v1/worksheets/:id/reject | worksheets.approve | Reject worksheet |
| PATCH | /api/v1/worksheets/:id/apply | worksheets.apply | Apply approved worksheet to products |
Reports
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/reports/sales-summary | reports.view_global | Sales summary (date_from, date_to, store_id) |
| GET | /api/v1/reports/tax | reports.view_global | Tax report |
| GET | /api/v1/reports/inventory-value | reports.view_global | Inventory valuation |
| GET | /api/v1/reports/x-report | reports.x_report | X report (session_id) |
| GET | /api/v1/reports/z-report | reports.z_report | Z report (session_id) |
| GET | /api/v1/reports/product-performance | reports.product_performance | Product performance |
| GET | /api/v1/reports/sales-by-period | reports.view_global | Sales by period |
| GET | /api/v1/reports/sales-by-rep | reports.sales_by_rep | Sales by representative |
| GET | /api/v1/reports/profit-margin | reports.profit_margin | Profit margin by department |
| GET | /api/v1/reports/cashier-performance | reports.cashier_performance | Cashier performance |
| GET | /api/v1/reports/department-sales | reports.view_global | Sales by department |
| GET | /api/v1/reports/inventory-turnover | reports.view_global | Inventory turnover |
| GET | /api/v1/reports/discount-analysis | reports.discount_analysis | Discount usage analysis |
Sync (HQ Server Only)
These endpoints use sync token authentication (not user JWT), except for the dashboard endpoints which use user JWT.
Store-Facing Endpoints (Sync Token Auth)
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/sync/pull | Sync Token | Pull entity updates from HQ |
| POST | /api/v1/sync/push | Sync Token | Push store data to HQ |
| GET | /api/v1/sync/status | Sync Token | Get sync status for the store |
| WebSocket | /api/v1/sync/ws | Sync Token | Real-time sync notifications |
Dashboard Endpoints (User JWT Auth)
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/sync/dashboard | JWT | Overview of all stores' sync status |
| GET | /api/v1/sync/errors | JWT | List unresolved sync errors |
| POST | /api/v1/sync/retry/:id | JWT | Retry a failed sync entry |
| POST | /api/v1/sync/resolve/:id | JWT | Mark failed entry as resolved |
| POST | /api/v1/sync/retry-all | JWT | Retry all failed entries (optionally by store) |
Health Check
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health | No | Returns { status: 'ok' } (store also includes storeId, storeCode) |