Skip to content

M7 API Reference

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.


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 state

All dashboard functions use method: "GET" and require no input. They return aggregated data for the dashboard KPI cards and charts.

Returns top-level KPI metrics.

Response

FieldTypeDescription
totalAssetsnumberTotal asset records
totalEmployeesnumberTotal employee records
totalDepartmentsnumberTotal department records
assignedAssetsnumberAssets with an assigned employee
unassignedAssetsnumberAssets in the pool (no assignee)
warrantyExpirednumberAssets with a past warranty expiry date
warrantySoonExpiringnumberAssets with warranty expiring within 90 days
warrantyActivenumberAssets with a future warranty expiry date
noWarrantyInfonumberAssets with no warranty expiry date recorded
totalInvestmentnumberSum of all purchaseCost values

Response: Array<{ category: AssetCategory; count: number }>

Returns asset count per category for the pie chart. Categories: LAPTOP, PHONE, MONITOR, PERIPHERAL, STORAGE, DOCKING_STATION, OTHER.


Response: Array<{ brand: string; count: number }>

Returns asset count per manufacturer, ordered by count descending.


Response: Array<{ status: AssetStatus; count: number }>

Returns asset count per status. Statuses: WORKING, BROKEN, IN_REPAIR, DECOMMISSIONED, LOST, UNASSIGNED.


Response: Array<{ id: string; name: string; employeeCount: number; assetCount: number }>

Returns employee and asset counts per department.


Response: Last 20 assets ordered by purchaseDate descending.

FieldType
idstring
assetIdstring (readable ID, e.g. HW-00001)
namestring
categoryAssetCategory
manufacturerstring | null
purchaseDateDateTime | null
purchaseCostnumber | null
statusAssetStatus
assignedTo{ id, name } | null
department{ id, name } | null

Response: Assets with warranty expiring within 90 days or already expired.

FieldType
idstring
assetIdstring
namestring
categoryAssetCategory
manufacturerstring | null
warrantyExpiryDateTime
isExpiredboolean
daysUntilExpirynumber (negative = already expired)
statusAssetStatus
assignedTo{ id, name } | null

Returns assets older than a given age threshold.

Input

FieldTypeDescription
yearsThresholdnumberReturn assets purchased more than this many years ago

Response: Array of full asset records plus yearsOld: number.


Response

{
byDepartment: Array<{ departmentId: string; name: string; totalCost: number; assetCount: number }>;
byEmployee: Array<{ employeeId: string; name: string; totalCost: number; assetCount: number }>;
}

Method: GET — Paginated asset inventory with search and filtering.

Input

FieldTypeDefaultDescription
searchstring?Full-text search on name, assetId, manufacturer, modelNumber, specifications
categoryAssetCategory?Filter by asset category
statusAssetStatus?Filter by asset status
departmentIdstring?Filter by department
assignedToIdstring?Filter by assigned employee
unassignedOnlyboolean?falseReturn only unassigned assets
pagenumber1Page number
pageSizenumber20Items per page
sortBystring"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)
}

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 record
  • department: Department record
  • transferHistory: Array of TransferHistory records (with fromEmployee, toEmployee)
  • checkoutForms: Array of CheckoutForm records (with employee)

Method: POST — Create a new asset record.

Input (all fields unless marked optional)

FieldTypeNotes
namestringRequired
categoryAssetCategoryRequired
statusAssetStatusRequired
conditionAssetConditionRequired
manufacturerstring?
modelNumberstring?
assetTagstring?Serial number / physical tag
specificationsstring?Free-text hardware specs
operatingSystemstring?
purchaseDateDateTime?
purchaseCostnumber?
warrantyExpiryDateTime?
locationstring?Physical location
notesstring?
departmentIdstring?
assignedToIdstring?
batteryConditionBatteryCondition?Phones/laptops
networkPhonestring?Carrier info
photoUrlstring?Condition photo URL
productLinkstring?Product page URL
ramGbnumber?Parsed spec
storageGbnumber?Parsed spec
storageUsedGbnumber?Parsed spec
screenSizeInchnumber?Parsed spec
cpuInfostring?Parsed spec
imeistring?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.


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.


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.


Method: POST — Assign an asset to an employee, or unassign it (return to pool).

Input

FieldTypeDescription
assetIdstringInternal asset id
employeeIdstring?Target employee id. Omit to unassign.
reasonTransferReasonReason for the transfer
locationstring?Target location
notesstring?Transfer notes

Response: { success: true }

Side effects:

  • Updates Asset.assignedToId and Asset.status
  • Creates a TransferHistory record
  • Creates an ActivityLog entry with action ASSET_ASSIGNED or ASSET_UNASSIGNED

Method: GET — Paginated employee directory.

Input

FieldTypeDefault
searchstring?
departmentIdstring?
isActiveboolean?
pagenumber1
pageSizenumber20
sortBystring"name"
sortOrder"asc" | "desc""asc"

Response: { employees: Employee[], total: number } — employees include asset count.


Method: GET — Fetch a single employee with full related data.

Input: { id: string }

Response: Full Employee record including:

  • department: Department record
  • assets: All currently assigned assets
  • transfersFrom / transfersTo: Transfer history
  • checkoutForms: Checkout history

Method: POST

Input

FieldType
namestring (required)
emailstring?
phonestring?
rolestring?
locationstring?
departmentIdstring?
hireDateDateTime?

Response: Created Employee record.

Side effects: Creates an ActivityLog entry with action EMPLOYEE_CREATED.


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.


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.


Method: GET — Lightweight list for assignment dropdowns.

Response: Array<{ id: string; name: string }>


Method: GET

Response: Array<{ id: string; name: string; createdAt: DateTime; updatedAt: DateTime }>


Method: POST — Input: { name: string }Response: Created department.

Method: POST — Input: { id: string; name: string }Response: Updated department.

Method: POST — Input: { id: string }Response: { success: true }

Method: GET — Response: Array<{ id: string; name: string }>


Method: GET — Paginated transfer records.

Input

FieldTypeDescription
assetIdstring?Filter by asset
employeeIdstring?Filter by employee (from or to)
pagenumber
pageSizenumber

Response: { transfers: TransferHistory[]; total: number } — each record includes asset, fromEmployee, toEmployee.


Method: POST — Create a new checkout record for temporary equipment loans.

Input

FieldType
assetIdstring (required)
employeeIdstring (required)
expectedReturnDateTime?
conditionOnCheckoutstring?
notesstring?

Response: Created CheckoutForm with status PENDING.

Side effects: Creates an ActivityLog entry with action CHECKOUT_CREATED.


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.


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.


Method: GET — Paginated audit trail.

Input: { page: number; pageSize: number }

Response

{
logs: ActivityLog[];
total: number;
page: number;
totalPages: number;
}

Each log entry includes:

FieldTypeDescription
idstring
actionActivityActionSee enum below
entityTypestringe.g. "Asset", "Employee", "CheckoutForm"
entityIdstringID of the affected record
descriptionstringHuman-readable description
metadataJSON?Structured extra context
createdAtDateTimeTimestamp

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


Method: GET — Fetch data formatted for PDF/CSV export.

Input

FieldTypeDescription
type"employee" | "global"Export scope
employeeIdstring?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.


Method: GET — Returns all assets with no assigned employee.

Response: Array of assets with fields: id, assetId, name, category, condition, batteryCondition, specifications, purchaseCost.


ValueDescription
LAPTOPLaptops and notebooks
PHONEMobile devices
MONITORDisplay screens
PERIPHERALKeyboards, mice, webcams, headsets, etc.
STORAGEExternal drives, USB sticks
DOCKING_STATIONUSB-C / Thunderbolt hubs
OTHERAnything not in the above
ValueDescription
WORKINGFully operational
BROKENNon-functional, awaiting decision
IN_REPAIRCurrently being repaired
DECOMMISSIONEDRetired from service
LOSTAsset cannot be located
UNASSIGNEDIn the pool, not assigned
ValueDescription
GOODLike new or excellent condition
NORMALNormal wear and tear
FAIRVisible wear, still functional
POORHeavy wear or damage
ABNORMALUnusual condition requiring attention

NORMAL · LOW · ABNORMAL · SERVICE_RECOMMENDED · UNKNOWN

NEW_ASSIGNMENT · ROLE_CHANGE · EMPLOYEE_DEPARTURE · REASSIGNMENT · REPAIR · RETURN_TO_POOL · BULK_IMPORT · OTHER

PENDING · ACKNOWLEDGED · RETURNED · CANCELLED


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 photoUrl asset condition photos
  • PDF generation — for employee equipment export sheets (currently returns raw data)
  • QR code serviceqrCode field is reserved in the schema
  • Email / SMS — for checkout acknowledgement and warranty alert notifications