REST API Assets - edmozley/freeitsm GitHub Wiki

๐Ÿ’ป REST API: Assets

The complete usage guide for the Assets module of the REST API โ€” every URL, method, parameter and response shape. This mirrors the interactive documentation at System โ†’ API โ†’ Documentation, where each endpoint also has a live "Try it" tester.

[!NOTE] New to the API? Read the basics table on REST API: Tickets first โ€” base URL, authentication, response envelope, error codes, rate limits and PATCH semantics are identical across all modules.

Three things that make Assets different from Tickets:

๐ŸŒ Install-wide Assets have no company โ€” a key's company scope does not restrict them (that machinery applies to tickets).
๐Ÿท๏ธ Hostname is identity The inventory agent, Intune sync and this API all treat hostname as the asset's identity. Creating a duplicate hostname returns 409 pointing at the existing asset โ€” PATCH that instead.
๐Ÿšซ No delete There is deliberately no DELETE /assets/{id} โ€” nothing in FreeITSM deletes assets (they're agent-maintained records). Retire an asset by setting its status.

๐Ÿš€ Quick start

Sync an asset from any external source (procurement system, another RMM, a spreadsheet):

curl -X POST "https://your-server/api/v1/assets" \
  -H "Authorization: Bearer fitsm_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
        "hostname": "LT-0042",
        "manufacturer": "Dell",
        "model": "Latitude 5440",
        "service_tag": "ABC1234",
        "asset_type_id": 4,
        "warranty_expiry": "2028-06-30",
        "purchase_cost": 899.99
      }'

Every change you make afterwards lands in the asset's History exactly as if an analyst edited it in the UI. ๐Ÿ“’


๐Ÿ’ป Assets

๐ŸŸข GET /assets โ€” list / search ย  ๐Ÿ”‘ assets.read

Parameter Description
q Search hostname, service tag, model and manufacturer
hostname / service_tag Exact lookups (handy for sync scripts)
asset_type_id ยท asset_status_id ยท location_id ยท supplier_id ยท cost_centre_id Classification filters
assigned_user_id Assets assigned to a requester ยท unassigned=true finds unassigned assets
warranty_within_days โณ Warranty expiring within N days โ€” the dashboard's "expiring soon" shape
warranty_expired true = warranty already lapsed
not_seen_days ๐Ÿ‘ป Not reported by the inventory agent for N days (or never seen)
sort hostname (default), id, first_seen, last_seen, warranty_expiry, purchase_date, model โ€” prefix - for descending
page / per_page Pagination โ€” default 1 / 25, max per_page 100
GET /assets?warranty_within_days=30&sort=warranty_expiry     โ† renewal report
GET /assets?not_seen_days=14&asset_status_id=1               โ† agents gone quiet
GET /assets?service_tag=ABC1234                              โ† exact serial lookup

The asset shape (list and detail are the same, detail adds assigned_users):

{ "id": 568, "hostname": "LT-0042",
  "type":     { "id": 4, "name": "Laptop" },
  "status":   { "id": 2, "name": "In use" },
  "location": { "id": 2, "name": "London", "path": "UK โ€บ London" },
  "hardware": { "manufacturer": "Dell", "model": "Latitude 5440", "service_tag": "ABC1234",
                "memory": 17179869184, "cpu_name": null, "speed": null, "gpu_name": null,
                "bios_version": null, "tpm_version": null, "bitlocker_status": null },
  "os":       { "operating_system": "Windows 11 Pro", "feature_release": null, "build_number": null },
  "network":  { "domain": null, "logged_in_user": null },
  "lifecycle": { "purchase_date": null, "purchase_cost": 899.99,
                 "supplier": { "id": 3, "name": "TechSupplies Ltd" },
                 "order_number": "PO-1001", "warranty_expiry": "2028-06-30" },
  "assigned_users_count": 1,
  "first_seen": "2026-07-02T22:59:12Z", "last_seen": "2026-07-02T22:59:12Z", "last_boot_at": null }

๐Ÿ”ต POST /assets โ€” create ย  ๐Ÿ”‘ assets.create

Only hostname is required (max 50 chars, unique โ€” duplicate โ†’ 409 with the existing asset's id). Every field from the PATCH table below can be set at creation. first_seen/last_seen are stamped automatically; a creation entry is written to the asset history; setting warranty_expiry re-syncs the calendar's warranty events. Returns 201 with the full asset.

asset_tag (3.1.0). Send one and it is kept, if it is free in the asset's company (409 naming the asset that has it otherwise). Leave it out and, if the company has automatic asset tags switched on, the asset gets the next one in sequence โ€” the response carries it. See Asset tag numbering โ€” Developer Guide.

๐ŸŸข GET /assets/{id} โ€” get one ย  ๐Ÿ”‘ assets.read

The full asset plus its current holders inline:

"assigned_users": [ { "user_id": 174, "name": "Jane Smith", "email": "[email protected]",
                      "assigned_at": "2026-07-02T22:59:36Z",
                      "expected_return_date": "2026-08-01", "notes": "Loan laptop" } ]

๐ŸŸ  PATCH /assets/{id} โ€” update ย  ๐Ÿ”‘ assets.update

Send only what changes; explicit null clears a field. Every change is audited into the asset's History with the same stable field keys the UI writes (so API edits render identically in the History modal), lookup changes are audited by display name ("Laptop", "UK โ€บ London"), and a warranty_expiry change re-syncs the calendar. Unknown lookup ids โ†’ 422; a hostname clash โ†’ 409. Re-sending the same PATCH is idempotent.

Group Fields
Classification asset_type_id ยท asset_status_id ยท location_id
Lifecycle purchase_date ยท purchase_cost ยท supplier_id ยท order_number ยท warranty_expiry (dates YYYY-MM-DD) ยท cost_centre_id (3.5.0 โ€” an active cost centre of the asset's company, 422 otherwise; one already held may be kept after it's made inactive). Read back as lifecycle.cost_centre: {id, code, name}
Identity & hardware hostname ยท manufacturer ยท model ยท service_tag ยท memory ยท cpu_name ยท speed ยท gpu_name ยท bios_version ยท tpm_version ยท bitlocker_status
OS & network operating_system ยท feature_release ยท build_number ยท domain ยท logged_in_user

[!TIP] The classification/lifecycle fields are what analysts edit in the UI; the hardware/OS fields are normally maintained by the inventory agent โ€” the API can write both, so a non-agent source (Intune export, another RMM) can keep assets in sync.


๐Ÿค Assignments (check-out / check-in)

Assignment is to requesters (end users), exactly like the UI: assigning writes the custody log's check-out and an assigned_user history entry; unassigning snapshots the holder and writes the check-in.

๐ŸŸข GET /assets/{id}/assignments ย  ๐Ÿ”‘ asset_assignments.read

Current holders with dates, due-back, notes and which analyst assigned them.

๐Ÿ”ต POST /assets/{id}/assignments ย  ๐Ÿ”‘ asset_assignments.create

Field Required Description
user_id or user_email โœ… An existing requester (create one first with POST /users โ€” needs users.create)
notes Assignment notes
expected_return_date Due back, YYYY-MM-DD

Already assigned โ†’ 409. Returns 201.

๐Ÿ”ด DELETE /assets/{id}/assignments/{user_id} ย  ๐Ÿ”‘ asset_assignments.delete

Checks the asset back in. Unknown assignment โ†’ 404.


๐Ÿ“’ History & custody

๐ŸŸข GET /assets/{id}/history ย  ๐Ÿ”‘ asset_history.read

Every audited change, newest first โ€” field key, old/new values (display names for lookups) and the analyst:

{ "data": [ { "id": 12, "field": "location", "old_value": null, "new_value": "London",
              "analyst": { "id": 1, "name": "Administrator" },
              "created_at": "2026-07-02T22:59:36Z" } ] }

๐ŸŸข GET /assets/{id}/custody ย  ๐Ÿ”‘ asset_history.read

The check-out / check-in trail โ€” who had the asset, when, expected return, processed by which analyst:

{ "data": [ { "id": 3, "action": "checkout", "user_id": 174, "user_name": "Jane Smith",
              "expected_return_date": "2026-08-01", "notes": "Loan",
              "by": "Administrator", "at": "2026-07-02T22:59:36Z" } ] }

๐Ÿงฐ Inventory (agent-collected)

Read-only views of what the inventory agent reports โ€” the same data as the asset detail pane's Storage/Devices/Software.

Endpoint Permission Returns
๐ŸŸข GET /assets/{id}/disks ๐Ÿ”‘ asset_inventory.read Drives with size/free bytes and used percent
๐ŸŸข GET /assets/{id}/network-adapters ๐Ÿ”‘ asset_inventory.read Adapters with MAC, IP, subnet, gateway, DHCP flag
๐ŸŸข GET /assets/{id}/devices ๐Ÿ”‘ asset_inventory.read Device-manager entries (class, name, status, driver)
๐ŸŸข GET /assets/{id}/software ๐Ÿ”‘ asset_inventory.read Installed software โ€” system components excluded unless ?include_components=true

๐Ÿ“š Reference data

All under the shared ๐Ÿ”‘ reference.read permission:

Endpoint Returns
๐ŸŸข GET /asset-types Asset types (id, name, description, is_active)
๐ŸŸข GET /asset-statuses Asset statuses
๐ŸŸข GET /asset-locations All locations, flat, each with parent_id and its full path ("UK โ€บ London โ€บ Office 1")
๐ŸŸข GET /suppliers Suppliers with display name and a supplies_assets flag

๐Ÿงช Worked example: warranty renewal report

A read-only key (assets: read + reference: read) pulls everything a renewals spreadsheet needs:

B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_โ€ฆ"

# Assets whose warranty expires in the next 90 days, soonest first
curl -s -H "$K" "$B/assets?warranty_within_days=90&sort=warranty_expiry&per_page=100" \
  | jq -r '.data[] | [.hostname, .hardware.service_tag, .lifecycle.warranty_expiry,
                      (.lifecycle.supplier.name // "-"), (.location.path // "-")] | @csv'

And the reverse direction โ€” a procurement system stamping newly purchased kit:

# Find the asset by serial, then set its lifecycle fields
ID=$(curl -s -H "$K" "$B/assets?service_tag=ABC1234" | jq -r '.data[0].id')
curl -s -X PATCH "$B/assets/$ID" -H "$K" -H "Content-Type: application/json" \
  -d '{"purchase_date":"2026-07-01","purchase_cost":899.99,"order_number":"PO-1001","warranty_expiry":"2029-07-01"}'

Under the hood: REST API โ€” How It Works ยท Tickets endpoints: REST API: Tickets ยท Keys & permissions: System โ†’ API (System module) ยท Module docs: Assets.