Assets
List asset change history
/a/Asset_changesGetDT
Page through the recorded custom-field changes for one asset.
When to use this endpoint
Use Asset_changesGetDT to reproduce the CRM1 asset History table.
It is a specialised positional DataTables feed. Use Asset_exportChanges when the user needs the matching CSV rather than a page of rows.
Authentication and permissions
The handler requires an authenticated tenant context, established by signed API-key fields or a CRM session. It checks the tenant table prefix but performs no page, role, or asset permission check.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
aid | Integer string | Yes | Asset ID. It is converted to an integer; existence, archived state, and soft-deleted state are not validated. |
length | Integer string | Yes | Page size. Zero and other integer-coercible false values become 10; negative values are passed into the SQL limit. |
draw | Integer string | Yes | DataTables draw token, returned as an integer. |
start | Integer string | No | Zero-based offset; defaults to 0. |
search | String | No | Scalar substring matched against field name, new value, old value, and change date. This route does not read DataTables’ nested search[value]. |
order[0][column] | Integer string | No | Sort column: 0 date, 1 user ID, 2 related object ID, 4 field name, or 5 new value. Other values leave database order unspecified. |
order[0][dir] | String | No | Exact asc selects ascending order; any other supplied value selects descending order. It has no effect when the column is unsupported. |
Example request
$page = FM_api('Asset_changesGetDT', [
'aid' => '4821',
'draw' => '3',
'start' => '0',
'length' => '25',
'search' => 'pressure',
'order' => [['column' => '0', 'dir' => 'desc']],
]);
Example response
{
"sql": "select ...",
"draw": 3,
"recordsTotal": "2",
"recordsFiltered": "1",
"data": [
["2026-09-24 10:15:00", "37", ["0", "1042"], ["7719", "2026-09-24 09:00:00"], "pressure", ["42", "39", 0]]
]
}
Each positional row is [changedAt, userId, [relatedType, relatedId], [jobId, localJobTime], fieldName, [newValue, oldValue, hideDiff]]. Database scalar values commonly remain strings.
hideDiff comes from the current asset-type template’s hide_diff flag for that field, not from the historical change row.
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"missing or invalid parameter: aid"} | aid is absent; zero is accepted when present. |
{"error":"missing or invalid parameter: length"} | length is absent. |
{"error":"missing or invalid parameter: draw"} | draw is absent. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Both counts are limited to the requested asset;
recordsFilteredadditionally appliessearch. Neither count checks whether the asset itself is deleted. - Job timestamps are converted from epoch seconds to the tenant’s local time. A missing or deleted linked job leaves the timestamp empty.
- The asset and asset type are read without deleted-state checks. A nonexistent asset can therefore produce PHP warnings even though an empty data set is the intended practical result.
- This read has no side effects.
See it in Fieldmotion
CRM1
Open an asset’s History tab and filter Network requests for Asset_changesGetDT. The page converts DataTables’ nested search value to this endpoint’s scalar search.
CRM2
No direct production CRM2 caller was found; CRM2 audit views use the newer log APIs.
Related endpoints
Asset_exportChangesdownloads the same history category as CSV.Asset_setCustomcan create these rows when the asset-type field records changes.