M7 API Reference
Overview
Section titled “Overview”M7 uses TanStack Start server functions (createServerFn()) as its API layer. These are typed RPC calls — not traditional REST endpoints — that run exclusively on the server and are invoked from client components with full TypeScript type safety.
All inputs are validated with Zod schemas before the handler executes. Errors throw exceptions, which are caught by TanStack Start’s error boundary.
Authentication: Not yet implemented. All server functions are publicly accessible with no auth middleware, session checks, or JWT validation.
API Request Flow
Section titled “API Request Flow”sequenceDiagram participant C as Client Component participant Q as TanStack Query participant SF as Server Function participant ZOD as Zod Validator participant PG as Prisma / PostgreSQL participant AL as ActivityLog
C->>Q: useQuery / useMutation Q->>SF: createServerFn() RPC call SF->>ZOD: inputValidator(schema) alt Validation fails ZOD-->>C: ZodError (400-equivalent) end ZOD->>PG: prisma.model.operation() PG-->>SF: Result SF->>AL: createActivityLog() [writes only] SF-->>Q: Typed response Q-->>C: Data / error stateDashboard & Analytics
Section titled “Dashboard & Analytics”All dashboard functions use method: "GET" and require no input. They return aggregated data for the dashboard KPI cards and charts.
getDashboardStats()
Section titled “getDashboardStats()”Returns top-level KPI metrics.
Response
| Field | Type | Description |
|---|---|---|
totalAssets | number | Total asset records |
totalEmployees | number | Total employee records |
totalDepartments | number | Total department records |
assignedAssets | number | Assets with an assigned employee |
unassignedAssets | number | Assets in the pool (no assignee) |
warrantyExpired | number | Assets with a past warranty expiry date |
warrantySoonExpiring | number | Assets with warranty expiring within 90 days |
warrantyActive | number | Assets with a future warranty expiry date |
noWarrantyInfo | number | Assets with no warranty expiry date recorded |
totalInvestment | number | Sum of all purchaseCost values |
getCategoryBreakdown()
Section titled “getCategoryBreakdown()”Response: Array<{ category: AssetCategory; count: number }>
Returns asset count per category for the pie chart. Categories: LAPTOP, PHONE, MONITOR, PERIPHERAL, STORAGE, DOCKING_STATION, OTHER.
getBrandBreakdown()
Section titled “getBrandBreakdown()”Response: Array<{ brand: string; count: number }>
Returns asset count per manufacturer, ordered by count descending.
getStatusBreakdown()
Section titled “getStatusBreakdown()”Response: Array<{ status: AssetStatus; count: number }>
Returns asset count per status. Statuses: WORKING, BROKEN, IN_REPAIR, DECOMMISSIONED, LOST, UNASSIGNED.
getDepartmentBreakdown()
Section titled “getDepartmentBreakdown()”Response: Array<{ id: string; name: string; employeeCount: number; assetCount: number }>
Returns employee and asset counts per department.
getRecentPurchases()
Section titled “getRecentPurchases()”Response: Last 20 assets ordered by purchaseDate descending.
| Field | Type |
|---|---|
id | string |
assetId | string (readable ID, e.g. HW-00001) |
name | string |
category | AssetCategory |
manufacturer | string | null |
purchaseDate | DateTime | null |
purchaseCost | number | null |
status | AssetStatus |
assignedTo | { id, name } | null |
department | { id, name } | null |
getWarrantyAlerts()
Section titled “getWarrantyAlerts()”Response: Assets with warranty expiring within 90 days or already expired.
| Field | Type |
|---|---|
id | string |
assetId | string |
name | string |
category | AssetCategory |
manufacturer | string | null |
warrantyExpiry | DateTime |
isExpired | boolean |
daysUntilExpiry | number (negative = already expired) |
status | AssetStatus |
assignedTo | { id, name } | null |
getAgingFleetReport(data)
Section titled “getAgingFleetReport(data)”Returns assets older than a given age threshold.
Input
| Field | Type | Description |
|---|---|---|
yearsThreshold | number | Return assets purchased more than this many years ago |
Response: Array of full asset records plus yearsOld: number.
getCostAnalysis()
Section titled “getCostAnalysis()”Response
{ byDepartment: Array<{ departmentId: string; name: string; totalCost: number; assetCount: number }>; byEmployee: Array<{ employeeId: string; name: string; totalCost: number; assetCount: number }>;}Assets
Section titled “Assets”listAssets(data)
Section titled “listAssets(data)”Method: GET — Paginated asset inventory with search and filtering.
Input
| Field | Type | Default | Description |
|---|---|---|---|
search | string? | — | Full-text search on name, assetId, manufacturer, modelNumber, specifications |
category | AssetCategory? | — | Filter by asset category |
status | AssetStatus? | — | Filter by asset status |
departmentId | string? | — | Filter by department |
assignedToId | string? | — | Filter by assigned employee |
unassignedOnly | boolean? | false | Return only unassigned assets |
page | number | 1 | Page number |
pageSize | number | 20 | Items per page |
sortBy | string | "createdAt" | Field to sort by |
sortOrder | "asc" | "desc" | "desc" | Sort direction |
Response
{ assets: Asset[]; // Full asset records with assignedTo and department total: number; // Total matching records (for pagination)}getAsset(data)
Section titled “getAsset(data)”Method: GET — Fetch a single asset with full related data.
Input: { id: string } — The asset’s internal id (cuid).
Response: Full Asset record including:
assignedTo: Employee recorddepartment: Department recordtransferHistory: Array ofTransferHistoryrecords (withfromEmployee,toEmployee)checkoutForms: Array ofCheckoutFormrecords (withemployee)
createAsset(data)
Section titled “createAsset(data)”Method: POST — Create a new asset record.
Input (all fields unless marked optional)
| Field | Type | Notes |
|---|---|---|
name | string | Required |
category | AssetCategory | Required |
status | AssetStatus | Required |
condition | AssetCondition | Required |
manufacturer | string? | — |
modelNumber | string? | — |
assetTag | string? | Serial number / physical tag |
specifications | string? | Free-text hardware specs |
operatingSystem | string? | — |
purchaseDate | DateTime? | — |
purchaseCost | number? | — |
warrantyExpiry | DateTime? | — |
location | string? | Physical location |
notes | string? | — |
departmentId | string? | — |
assignedToId | string? | — |
batteryCondition | BatteryCondition? | Phones/laptops |
networkPhone | string? | Carrier info |
photoUrl | string? | Condition photo URL |
productLink | string? | Product page URL |
ramGb | number? | Parsed spec |
storageGb | number? | Parsed spec |
storageUsedGb | number? | Parsed spec |
screenSizeInch | number? | Parsed spec |
cpuInfo | string? | Parsed spec |
imei | string? | Mobile devices |
Response: Created Asset record. The assetId (readable format HW-XXXXX) is auto-generated.
Side effects: Creates an ActivityLog entry with action ASSET_CREATED.
updateAsset(data)
Section titled “updateAsset(data)”Method: POST — Update an existing asset.
Input: { id: string } plus any subset of the fields listed in createAsset.
Response: Updated Asset record.
Side effects: Creates an ActivityLog entry with action ASSET_UPDATED.
deleteAsset(data)
Section titled “deleteAsset(data)”Method: POST — Permanently delete an asset.
Input: { id: string }
Response: { success: true }
Side effects: Cascades to TransferHistory and CheckoutForm records. Creates an ActivityLog entry with action ASSET_DELETED.
assignAsset(data)
Section titled “assignAsset(data)”Method: POST — Assign an asset to an employee, or unassign it (return to pool).
Input
| Field | Type | Description |
|---|---|---|
assetId | string | Internal asset id |
employeeId | string? | Target employee id. Omit to unassign. |
reason | TransferReason | Reason for the transfer |
location | string? | Target location |
notes | string? | Transfer notes |
Response: { success: true }
Side effects:
- Updates
Asset.assignedToIdandAsset.status - Creates a
TransferHistoryrecord - Creates an
ActivityLogentry with actionASSET_ASSIGNEDorASSET_UNASSIGNED
Employees
Section titled “Employees”listEmployees(data)
Section titled “listEmployees(data)”Method: GET — Paginated employee directory.
Input
| Field | Type | Default |
|---|---|---|
search | string? | — |
departmentId | string? | — |
isActive | boolean? | — |
page | number | 1 |
pageSize | number | 20 |
sortBy | string | "name" |
sortOrder | "asc" | "desc" | "asc" |
Response: { employees: Employee[], total: number } — employees include asset count.
getEmployee(data)
Section titled “getEmployee(data)”Method: GET — Fetch a single employee with full related data.
Input: { id: string }
Response: Full Employee record including:
department: Department recordassets: All currently assigned assetstransfersFrom/transfersTo: Transfer historycheckoutForms: Checkout history
createEmployee(data)
Section titled “createEmployee(data)”Method: POST
Input
| Field | Type |
|---|---|
name | string (required) |
email | string? |
phone | string? |
role | string? |
location | string? |
departmentId | string? |
hireDate | DateTime? |
Response: Created Employee record.
Side effects: Creates an ActivityLog entry with action EMPLOYEE_CREATED.
updateEmployee(data)
Section titled “updateEmployee(data)”Method: POST — Update an existing employee.
Input: { id: string } plus any subset of createEmployee fields.
Response: Updated Employee record.
Side effects: Creates an ActivityLog entry with action EMPLOYEE_UPDATED.
deleteEmployee(data)
Section titled “deleteEmployee(data)”Method: POST — Deactivate (soft-delete) an employee.
Input: { id: string }
Response: { success: true }
Side effects: Sets Employee.isActive = false. Creates an ActivityLog entry with action EMPLOYEE_DEACTIVATED.
getEmployeeOptions()
Section titled “getEmployeeOptions()”Method: GET — Lightweight list for assignment dropdowns.
Response: Array<{ id: string; name: string }>
Departments
Section titled “Departments”listDepartments()
Section titled “listDepartments()”Method: GET
Response: Array<{ id: string; name: string; createdAt: DateTime; updatedAt: DateTime }>
createDepartment(data)
Section titled “createDepartment(data)”Method: POST — Input: { name: string } — Response: Created department.
updateDepartment(data)
Section titled “updateDepartment(data)”Method: POST — Input: { id: string; name: string } — Response: Updated department.
deleteDepartment(data)
Section titled “deleteDepartment(data)”Method: POST — Input: { id: string } — Response: { success: true }
getDepartmentOptions()
Section titled “getDepartmentOptions()”Method: GET — Response: Array<{ id: string; name: string }>
Transfer History
Section titled “Transfer History”getTransferHistory(data)
Section titled “getTransferHistory(data)”Method: GET — Paginated transfer records.
Input
| Field | Type | Description |
|---|---|---|
assetId | string? | Filter by asset |
employeeId | string? | Filter by employee (from or to) |
page | number | — |
pageSize | number | — |
Response: { transfers: TransferHistory[]; total: number } — each record includes asset, fromEmployee, toEmployee.
Checkout Forms
Section titled “Checkout Forms”createCheckout(data)
Section titled “createCheckout(data)”Method: POST — Create a new checkout record for temporary equipment loans.
Input
| Field | Type |
|---|---|
assetId | string (required) |
employeeId | string (required) |
expectedReturn | DateTime? |
conditionOnCheckout | string? |
notes | string? |
Response: Created CheckoutForm with status PENDING.
Side effects: Creates an ActivityLog entry with action CHECKOUT_CREATED.
acknowledgeCheckout(data)
Section titled “acknowledgeCheckout(data)”Method: POST — Employee acknowledges receipt of checked-out equipment.
Input: { id: string; note?: string }
Response: Updated CheckoutForm with status ACKNOWLEDGED and acknowledgedAt timestamp.
Side effects: Creates an ActivityLog entry with action CHECKOUT_ACKNOWLEDGED.
returnCheckout(data)
Section titled “returnCheckout(data)”Method: POST — Mark equipment as returned.
Input: { id: string; conditionOnReturn?: string; returnNotes?: string }
Response: Updated CheckoutForm with status RETURNED and returnDate timestamp.
Side effects: Creates an ActivityLog entry with action CHECKOUT_RETURNED.
Activity Log
Section titled “Activity Log”getActivityLog(data)
Section titled “getActivityLog(data)”Method: GET — Paginated audit trail.
Input: { page: number; pageSize: number }
Response
{ logs: ActivityLog[]; total: number; page: number; totalPages: number;}Each log entry includes:
| Field | Type | Description |
|---|---|---|
id | string | — |
action | ActivityAction | See enum below |
entityType | string | e.g. "Asset", "Employee", "CheckoutForm" |
entityId | string | ID of the affected record |
description | string | Human-readable description |
metadata | JSON? | Structured extra context |
createdAt | DateTime | Timestamp |
ActivityAction enum values:
ASSET_CREATED, ASSET_UPDATED, ASSET_DELETED, ASSET_ASSIGNED, ASSET_UNASSIGNED, ASSET_TRANSFERRED, EMPLOYEE_CREATED, EMPLOYEE_UPDATED, EMPLOYEE_DEACTIVATED, CHECKOUT_CREATED, CHECKOUT_ACKNOWLEDGED, CHECKOUT_RETURNED, BULK_IMPORT
Export & Utilities
Section titled “Export & Utilities”getExportData(data)
Section titled “getExportData(data)”Method: GET — Fetch data formatted for PDF/CSV export.
Input
| Field | Type | Description |
|---|---|---|
type | "employee" | "global" | Export scope |
employeeId | string? | Required when type = "employee" |
Response (employee): Employee record with all assigned assets, formatted for a per-person equipment sheet.
Response (global): All assets with assigned employee and department info, formatted for a full inventory export.
getUnassignedPool()
Section titled “getUnassignedPool()”Method: GET — Returns all assets with no assigned employee.
Response: Array of assets with fields: id, assetId, name, category, condition, batteryCondition, specifications, purchaseCost.
Enum Reference
Section titled “Enum Reference”AssetCategory
Section titled “AssetCategory”| Value | Description |
|---|---|
LAPTOP | Laptops and notebooks |
PHONE | Mobile devices |
MONITOR | Display screens |
PERIPHERAL | Keyboards, mice, webcams, headsets, etc. |
STORAGE | External drives, USB sticks |
DOCKING_STATION | USB-C / Thunderbolt hubs |
OTHER | Anything not in the above |
AssetStatus
Section titled “AssetStatus”| Value | Description |
|---|---|
WORKING | Fully operational |
BROKEN | Non-functional, awaiting decision |
IN_REPAIR | Currently being repaired |
DECOMMISSIONED | Retired from service |
LOST | Asset cannot be located |
UNASSIGNED | In the pool, not assigned |
AssetCondition
Section titled “AssetCondition”| Value | Description |
|---|---|
GOOD | Like new or excellent condition |
NORMAL | Normal wear and tear |
FAIR | Visible wear, still functional |
POOR | Heavy wear or damage |
ABNORMAL | Unusual condition requiring attention |
BatteryCondition
Section titled “BatteryCondition”NORMAL · LOW · ABNORMAL · SERVICE_RECOMMENDED · UNKNOWN
TransferReason
Section titled “TransferReason”NEW_ASSIGNMENT · ROLE_CHANGE · EMPLOYEE_DEPARTURE · REASSIGNMENT · REPAIR · RETURN_TO_POOL · BULK_IMPORT · OTHER
CheckoutStatus
Section titled “CheckoutStatus”PENDING · ACKNOWLEDGED · RETURNED · CANCELLED
Third-Party Integrations
Section titled “Third-Party Integrations”Note: No third-party API integrations are currently implemented. The application only connects to its own PostgreSQL database. There are no external service calls, webhooks, or background job integrations in the current codebase.
Future integration candidates identified in the schema and UI:
- Cloud storage (S3 or similar) — for
photoUrlasset condition photos - PDF generation — for employee equipment export sheets (currently returns raw data)
- QR code service —
qrCodefield is reserved in the schema - Email / SMS — for checkout acknowledgement and warranty alert notifications