API Reference
Assets

Assets

The assets API manages your organization's equipment inventory - laptops, monitors, phones, badges - and tracks recovery when someone leaves.

Base path: /api/assets


List assets

GET /api/assets

Query parameters

ParamTypeDescription
limitintegerResults per page (default 100, max 500)
offsetintegerRow offset for pagination (default 0)

There's no status or assignedTo filter on this endpoint - filtering happens client-side, or you can page through everything and filter yourself.

Response 200 OK

{
  "rows": [
    {
      "id": "8f14e45f-...",
      "org_id": "org_xyz",
      "org_employee_id": "9bb58f26-...",
      "item_name": "MacBook Pro 14\"",
      "serial_number": "C02XY1234",
      "model": "MacBook Pro M3 14-inch",
      "category": "laptop",
      "condition": "good",
      "tracking_number": "",
      "return_deadline": "2024-06-15",
      "status": "deployed",
      "created_at": "2023-09-01T00:00:00.000Z",
      "updated_at": "2023-09-01T00:00:00.000Z",
      "employee": {
        "id": "9bb58f26-...",
        "full_name": "Jordan Lee",
        "email": "jordan@acme.com",
        "employee_id": "EMP-1042",
        "department": "Engineering"
      }
    }
  ],
  "total": 38,
  "limit": 100,
  "offset": 0
}

Fields come straight off the assets table - snake_case throughout, not camelCase. employee is joined in from org_employees and will be null-ish (all fields null) if the asset isn't assigned to anyone.


Asset summary

GET /api/assets/summary

Org-wide rollup used by the unreturned-assets report: totals by status, a breakdown by category, and an aging bucket for overdue returns.

Response 200 OK

{
  "totals": { "total": 38, "deployed": 22, "returned": 15, "lost": 1 },
  "byCategory": [
    { "category": "laptop", "total": 20, "deployed": 12, "returned": 8 }
  ],
  "overdueAging": [
    { "bucket": "1-7d", "count": 3 },
    { "bucket": "8-30d", "count": 1 },
    { "bucket": "30d+", "count": 0 }
  ],
  "noDeadlineDeployedCount": 4
}

Create an asset

POST /api/assets

Admin only.

Request body

{
  "itemName": "MacBook Pro 14\"",
  "serialNumber": "C02XY1234",
  "model": "MacBook Pro M3 14-inch",
  "category": "laptop",
  "condition": "good",
  "orgEmployeeId": "9bb58f26-...",
  "trackingNumber": "",
  "returnDeadline": "2024-06-15"
}
FieldRequiredDescription
itemNameDevice / equipment name
serialNumberUnique serial or asset tag
model-Model name
category-laptop · monitor · phone · badge · peripheral · other (anything else is coerced to other)
condition-Free text, defaults to good
orgEmployeeId-Assign to an org employee record
trackingNumber-Shipping tracking number
returnDeadline-ISO date string

New assets are always created with status: "deployed". Fails with 409 if the serial number already exists, and 403 if your plan's asset limit is reached.

Response 201 Created - the created asset, including the joined employee object.


Bulk import assets

POST /api/assets/bulk

Admin only. Imports as many assets as your plan has room for; the rest are reported as skipped rather than rejecting the whole batch.

Request body

{
  "assets": [
    { "itemName": "MacBook Pro 14\"", "serialNumber": "C02XY1234", "category": "laptop" },
    { "itemName": "Dell Monitor", "serialNumber": "D5567", "category": "monitor" }
  ]
}

Response 201 Created

{
  "inserted": 1,
  "skipped": 1,
  "errors": [
    { "serialNumber": "D5567", "reason": "Duplicate serial number - already exists" }
  ]
}

Update an asset

PATCH /api/assets/:id

Admin only. This is a full edit, not a partial one - itemName and serialNumber are required on every call, even if unchanged. Accepts the same fields as create, plus status.

Response 200 OK - the updated asset, with the joined employee object.


Mark asset returned

PATCH /api/assets/:id/return

Admin or manager. Sets status to returned and touches updated_at - that's all it does.

⚠️

This endpoint doesn't take a request body. Any condition or notes you send are ignored; there's no field on the assets table to store return condition or notes today. If you need to record condition at return time, follow up with PATCH /api/assets/:id to set condition separately.

Response 200 OK - the full updated asset row (snake_case, same shape as the list endpoint).


Bulk mark returned

PATCH /api/assets/bulk-return

Admin or manager. Marks multiple assets returned in one call.

Request body

{ "ids": ["8f14e45f-...", "3c9c9a1e-..."] }

Response 200 OK

{ "updated": 2, "assets": [ /* updated asset rows */ ] }

Delete an asset

DELETE /api/assets/:id

Admin only.

Response 200 OK

{ "success": true }