Assets API
Enterprise feature
The Assets API is available on Enterprise plans. Compare plans to find the right fit for your team.
The Assets API lets you manage equipment records, track asset status, record service history, submit meter readings, and transfer assets between locations. Use it to integrate asset data with your CMMS, ERP, or IoT platforms.
Endpoints summary
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /api/v1/assets | assets:read | List assets |
GET | /api/v1/assets?id={id} | assets:read | Get a single asset |
POST | /api/v1/assets | assets:write | Create an asset |
PUT | /api/v1/assets?id={id} | assets:write | Update an asset |
PATCH | /api/v1/assets?id={id} | assets:write | Update asset status |
GET | /api/v1/assets?id={id}&sub=service_history | assets:read | Service history |
POST | /api/v1/assets?id={id}&sub=service | assets:service | Create a service record |
POST | /api/v1/assets?id={id}&sub=meter_reading | assets:meter | Submit a meter reading |
POST | /api/v1/assets?id={id}&sub=transfer | assets:transfer | Transfer an asset |
List assets
GET /api/v1/assets
Scope: assets:read
Returns a paginated list of assets.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (e.g., Active, In Maintenance) |
asset_type_id | integer | Filter by asset type |
condition | string | Filter by asset condition |
location_id | integer | Filter by location where the asset is installed |
ownership_type | string | Filter by ownership type (e.g., company-owned, leased) |
search | string | Search by asset name, tag, or serial number |
created_after | string | Filter assets created after this date (ISO 8601 format) |
created_before | string | Filter assets created before this date (ISO 8601 format) |
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 50, max: 100) |
Example request
curl -X GET "https://fsmnavigator.com/api/v1/assets?status=Active&per_page=50" \
-H "X-API-Key: YOUR_API_KEY"
Example response
{
"data": [
{
"asset_id": 123,
"asset_tag": "AST-000123",
"asset_name": "Rooftop HVAC Unit #3",
"asset_type_id": 5,
"asset_type_name": "HVAC Unit",
"status": "Active",
"location_id": 87,
"location_name": "Main Office — Acme Corp",
"assigned_to": 45,
"assigned_to_name": "Alex Johnson",
"serial_number": "HVAC-2025-04821",
"model": "ClimateMax 5000",
"manufacturer": "CoolTech Industries",
"purchase_date": "2025-06-15",
"purchase_cost": 12500.00,
"warranty_expiry": "2028-06-15",
"notes": "Installed on building rooftop, zone B",
"created_at": "2025-08-15T04:00:00-05:00",
"updated_at": "2026-01-10T09:30:00-05:00"
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 1,
"total_pages": 1
},
"request_id": "a1b2c3d4e5f6"
}
Get a single asset
GET /api/v1/assets?id={id}
Scope: assets:read
Retrieves full details for one asset.
Example request
Example response
{
"data": {
"asset_id": 123,
"asset_tag": "AST-000123",
"asset_name": "Rooftop HVAC Unit #3",
"asset_type_id": 5,
"asset_type_name": "HVAC Unit",
"status": "Active",
"location_id": 87,
"location_name": "Main Office — Acme Corp",
"assigned_to": 45,
"assigned_to_name": "Alex Johnson",
"serial_number": "HVAC-2025-04821",
"model": "ClimateMax 5000",
"manufacturer": "CoolTech Industries",
"purchase_date": "2025-06-15",
"purchase_cost": 12500.00,
"warranty_expiry": "2028-06-15",
"notes": "Installed on building rooftop, zone B",
"created_at": "2025-08-15T04:00:00-05:00",
"updated_at": "2026-01-10T09:30:00-05:00"
},
"request_id": "a1b2c3d4e5f6"
}
Custom field values
A single asset also returns its custom field values in custom_fields, one entry per field that holds a value. A number field's value is a string with exactly four decimal places, such as "12.5000", so every digit you saved comes back unchanged. Read it as a decimal string rather than a floating-point number if you need the exact value.
"custom_fields": [
{
"field_id": 42,
"field_name": "BTU Rating",
"field_key": "btu_rating",
"field_type": "number",
"value": "60000.0000",
"updated_at": "2026-01-10T09:30:00-05:00"
}
]
Create an asset
POST /api/v1/assets
Scope: assets:write
Creates a new asset record. The asset_tag is auto-generated in AST-NNNNNN format — do not include it in your request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
asset_name | string | Yes | Descriptive name for the asset |
asset_type_id | integer | Yes | ID of the asset type |
serial_number | string | No | Manufacturer serial number |
model | string | No | Model name or number |
manufacturer | string | No | Manufacturer name |
purchase_date | string | No | Purchase date (YYYY-MM-DD) |
purchase_cost | number | No | Purchase cost |
status | string | No | Initial status (default: Active) |
location_id | integer | No | Location where the asset is installed |
assigned_to | integer | No | Technician responsible for this asset |
notes | string | No | Additional notes |
Example request
curl -X POST "https://fsmnavigator.com/api/v1/assets" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"asset_name": "Lobby Printer MFP-200",
"asset_type_id": 5,
"serial_number": "MFP200-2026-04821",
"model": "MFP-200",
"manufacturer": "PrintCo",
"purchase_date": "2026-02-01",
"purchase_cost": 2499.99,
"location_id": 87,
"notes": "Second floor lobby"
}'
Example response 201 Created
{
"success": true,
"message": "Asset created successfully",
"data": {
"asset_id": 124,
"asset_tag": "AST-000124",
"asset_name": "Lobby Printer MFP-200"
},
"request_id": "a1b2c3d4e5f6"
}
Update an asset
PUT /api/v1/assets?id={id}
Scope: assets:write
Updates one or more fields on an existing asset. Only include the fields you want to change.
You can include status alongside any other field. It goes through exactly the same checks as a PATCH — the value must be one the assets use, and the change must be a permitted transition from the asset's current status — and the move is recorded in the asset's status history just as a PATCH would record it. If the status is rejected, nothing at all is written: the other fields in the same request are left alone.
An asset that has been decommissioned cannot be updated through this endpoint at all; the request returns 409 Conflict with invalid_state.
Example request
curl -X PUT "https://fsmnavigator.com/api/v1/assets?id=123" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"asset_name": "Rooftop HVAC Unit #3 (Upgraded)",
"assigned_to": 45
}'
Example response
{
"success": true,
"message": "Asset updated successfully",
"data": {
"asset_id": 123,
"asset_name": "Rooftop HVAC Unit #3 (Upgraded)",
"status": "Active"
},
"request_id": "a1b2c3d4e5f6"
}
Update asset status
PATCH /api/v1/assets?id={id}
Scope: assets:write
Changes the status of an asset. Only valid transitions are allowed — see status transitions below.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | New status value |
Example request
curl -X PATCH "https://fsmnavigator.com/api/v1/assets?id=123" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "In Maintenance"
}'
Example response
{
"success": true,
"message": "Asset status updated to In Maintenance",
"data": {
"asset_id": 123,
"status": "In Maintenance",
"previous_status": "Active"
},
"request_id": "a1b2c3d4e5f6"
}
Status transitions
Not every status change is permitted. The table below shows allowed transitions:
| From status | Allowed transitions |
|---|---|
| Active | Idle, In Maintenance, In Transit, Retired, Lost, Non-Compliant, Decommissioned |
| Idle | Active, In Maintenance, In Transit, Retired, Decommissioned, Lost |
| In Maintenance | Active, Idle, Retired, Decommissioned |
| Pending Install | Active, Idle, In Transit, Non-Compliant |
| In Transit | Active, Idle, Pending Install |
| Lost | Active, Retired, Decommissioned |
| Non-Compliant | Active, Idle, In Maintenance, Retired, Decommissioned |
| Decommissioned | (terminal — no transitions) |
| Retired | Active, Decommissioned |
Tip
Use the PATCH method for status-only updates. To change other fields along with the status, use PUT instead.
Service history
GET /api/v1/assets?id={id}&sub=service_history
Scope: assets:read
Returns a paginated list of service records for an asset.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 50) |
Example request
curl -X GET "https://fsmnavigator.com/api/v1/assets?id=123&sub=service_history" \
-H "X-API-Key: YOUR_API_KEY"
Example response
{
"data": [
{
"service_id": 1,
"asset_id": 123,
"task_id": 456,
"service_type": "preventive_maintenance",
"description": "Quarterly filter replacement and system check",
"resolution_notes": "Replaced air filters, cleaned condenser coils",
"technician_id": 45,
"service_date": "2026-02-15",
"service_started_at": "2026-02-15T04:00:00-05:00",
"service_completed_at": "2026-02-15T06:30:00-05:00",
"next_service_due": "2026-05-15",
"meter_reading_at_service": 12450,
"labor_hours": 2.5,
"service_cost": 350.00,
"is_warranty_work": false,
"warranty_claim_number": null,
"parts_used_summary": [
{ "part": "HEPA filter", "qty": 2 },
{ "part": "Belt", "qty": 1 }
],
"created_at": "2026-02-15T06:35:00-05:00"
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 1,
"total_pages": 1
},
"request_id": "a1b2c3d4e5f6"
}
parts_used_summary is always a list, or null when no parts were recorded. A value that was saved as anything other than a JSON list (for example a plain string) is returned as an empty list [].
Create a service record
POST /api/v1/assets?id={id}&sub=service
Scope: assets:service
Logs a service event against an asset.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
service_type | string | Yes | Type of service — see service types |
description | string | Yes | Description of the service performed |
service_date | string | Yes | Date of service (YYYY-MM-DD) |
cost | number | No | Cost of the service (returned as service_cost) |
technician_id | integer | No | Technician who performed the service |
resolution_notes | string | No | Notes on the resolution or outcome |
labor_hours | number | No | Hours of labor spent |
is_warranty_work | boolean | No | Whether the service is covered by warranty |
warranty_claim_number | string | No | Warranty claim reference number |
next_service_due | string | No | Date the next service is due (YYYY-MM-DD) |
parts_used_summary | array | No | Parts used, as a JSON list (for example [{"part": "Belt", "qty": 1}]) |
Service types
| Value | Description |
|---|---|
repair | Corrective repair |
inspection | Routine or ad-hoc inspection |
preventive_maintenance | Scheduled preventive maintenance |
installation | Initial installation |
decommission | Removal from service |
meter_read | Meter reading during service |
warranty_claim | Service under warranty |
calibration | Instrument or device calibration |
upgrade | Hardware or firmware upgrade |
relocation | Physical relocation of the asset |
Example request
curl -X POST "https://fsmnavigator.com/api/v1/assets?id=123&sub=service" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service_type": "preventive_maintenance",
"description": "Quarterly HVAC filter replacement",
"service_date": "2026-02-15",
"cost": 350.00,
"technician_id": 45,
"resolution_notes": "Replaced filters, system running normally",
"labor_hours": 2.5
}'
Example response 201 Created
{
"success": true,
"message": "Service record created successfully",
"data": {
"service_id": 1,
"asset_id": 123,
"service_type": "preventive_maintenance"
},
"request_id": "a1b2c3d4e5f6"
}
Submit a meter reading
POST /api/v1/assets?id={id}&sub=meter_reading
Scope: assets:meter
Records a meter reading for an asset.
Prerequisite: configure meters first
Meters must be set up for the asset through the web dashboard before readings can be submitted via the API. If no meter exists for the given meter_id, the API returns a 400 error with the message "Meter not found or does not belong to this asset".
Request body
| Field | Type | Required | Description |
|---|---|---|---|
meter_id | integer | Yes | ID of the meter (configured in the dashboard) |
reading_value | number | Yes | Current meter reading |
reading_date | string | Yes | Date of the reading (YYYY-MM-DD) |
Example request
curl -X POST "https://fsmnavigator.com/api/v1/assets?id=123&sub=meter_reading" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"meter_id": 1,
"reading_value": 12450,
"reading_date": "2026-02-15"
}'
Transfer an asset
POST /api/v1/assets?id={id}&sub=transfer
Scope: assets:transfer
Initiates a transfer of an asset to a different location.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to_location_id | integer | Yes | Destination location ID |
transfer_reason | string | No | Reason for the transfer |
transfer_date | string | No | Date of transfer (YYYY-MM-DD) |
Example request
curl -X POST "https://fsmnavigator.com/api/v1/assets?id=123&sub=transfer" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to_location_id": 22,
"transfer_reason": "Relocating to new branch office"
}'
Example response
{
"success": true,
"message": "Asset transfer initiated successfully",
"data": {
"transfer_id": 1,
"asset_id": 123,
"from_location_id": 87,
"to_location_id": 22,
"status": "Completed"
},
"request_id": "a1b2c3d4e5f6"
}
Asset statuses
Assets follow a lifecycle with nine defined statuses:
| Status | Description |
|---|---|
| Active | In regular use |
| Idle | Not currently in use but available |
| In Maintenance | Undergoing scheduled or unscheduled maintenance |
| Pending Install | Awaiting installation at a location |
| In Transit | Being moved between locations |
| Lost | Cannot be located; under investigation |
| Non-Compliant | Fails compliance or certification requirements |
| Decommissioned | Permanently removed from service (terminal) |
| Retired | Reached end of useful life (terminal) |
Error responses
See Error codes for the full reference. Common asset-specific errors:
| HTTP code | Error | Description |
|---|---|---|
400 | validation_failed | Missing required fields or invalid data |
400 | invalid_parent_asset | parent_asset_id does not exist or belongs to another company |
409 | invalid_state | The asset has been decommissioned and can no longer be updated |
409 | invalid_status | The status you sent is not one assets use |
409 | invalid_transition | The status change is not allowed from the asset's current status |
409 | conflict | Someone else changed the asset's status while you were working on it |
400 | meter_not_found | Meter does not exist or does not belong to this asset |
409 | customer_inactive | The customer you linked the asset to has been removed. Restore the customer first. |
400 | invalid_meter_interval | Meter interval must be a positive number |
400 | invalid_frequency_interval | Maintenance frequency interval must be a positive whole number |
404 | not_found | Asset ID does not exist |
403 | feature_not_available | Enterprise plan required |
Related guides
- Endpoints overview — all available endpoints
- Authentication — API key setup
- Jobs API — link jobs to assets
- Managing assets — web dashboard guide
- Asset lifecycle — status transitions explained