Asset Lifecycle & Tracking Modes
The asset model distinguishes two things: the tracking mode of the asset type (PERSON / LOCATION / CONSUMABLE) and the lifecycle status of the individual asset. The mode determines the anchor (person or location) and which actions are possible; the status describes the current lifecycle state. On every write the server checks that mode, status and anchor are consistent. This page is the conceptual foundation; the concrete endpoints live in the Assets API.
🧭
Core Principle
The status permits or forbids an anchor — but the ACTION sets or clears it, never the status as a side effect. Anchor fields (assignedToId, locationId) change only through actions (checkout, return, install, deinstall, report lost, retire). Stock is the status AVAILABLE (available/ready). The physical location (locationId) is independent of the status and allowed in every status.
Tracking Modes (AssetType.trackingMode)
The mode is set on the asset type and applies to all assets of that type. It determines whether the type is a consumable and whether assets are bound to people or to locations. While the type has no assets the mode can be changed freely; after that the API answers HTTP 409.
| Mode |
Anchor |
User allowed? |
Assignment path |
Notes |
PERSON | User | ✅ | Checkout / handover | Unique item; locationId possible in addition (storage/office location) |
LOCATION | Location | ❌ never | Install / deinstall | no handover (a location cannot confirm) |
CONSUMABLE | Quantity | ❌ never | Quantity assignment (ConsumableAssignment) | reduced status set; no CMDB relations/handover; always standalone |
Location ≠ "installed":
An AVAILABLE asset may have a location (storage). "Installed" is the status IN_USE, not "has a locationId".
Status Taxonomy (11 statuses)
Every asset has exactly one of the following 11 statuses.
| Status |
Meaning |
User allowed? |
Typical anchor |
ORDERED | ordered, not delivered | ❌ | — |
RECEIVED | received, not yet ready | ❌ | — |
AVAILABLE | available/ready (stock), freely assignable | ❌ | optional locationId |
RESERVED | reserved — not yet handed out ("reserved for X" is really PENDING_ACCEPTANCE) | ❌ | — |
PENDING_ACCEPTANCE | person handover running, confirmation pending | ✅ (recipient) | User |
IN_USE | in use — PERSON: with the user / LOCATION: installed at the location | mode-dependent | User or location |
MAINTENANCE | in maintenance/repair — anchor may remain | yes (keeps user) | possibly user |
RETURN_PENDING | return running, user still holds it, awaiting IT | ✅ (User) | User |
RETIRED | retired (reactivatable) | ❌ | — |
LOST | lost/stolen (mandatory reason) | ❌ | locationId remains (last location) |
DISPOSED | disposed (final) | ❌ | — |
Status Groups and Their Rules
| Group | Members | Purpose |
| No user | ORDERED, RECEIVED, AVAILABLE, RESERVED, RETIRED, LOST, DISPOSED | no user allowed |
| Anchor required | IN_USE, PENDING_ACCEPTANCE, RETURN_PENDING | at least one anchor (user/location) |
| Checkout from | AVAILABLE, RESERVED, MAINTENANCE | starting status for checkout/install |
| Return target | AVAILABLE, MAINTENANCE, RETIRED | target status on return (checkin/confirmReturn) |
| Out of service | DISPOSED, RETIRED, LOST | no new license/contract links; the assignment must be removed |
| Reason required | LOST | mandatory comment (statusNote) |
| Consumable | ORDERED, RECEIVED, AVAILABLE, RETIRED, DISPOSED | allowed statuses for CONSUMABLE types |
| Manually settable | 9 (all except PENDING_ACCEPTANCE, RETURN_PENDING) | settable via form/update/bulk |
| Bulk settable | ORDERED, RECEIVED, AVAILABLE, RESERVED, MAINTENANCE, RETIRED, DISPOSED | settable via bulk; excluded are IN_USE (requires a handover record) and LOST (requires a reason) |
| Terminal | DISPOSED | no further transitions |
MAINTENANCE intentionally belongs neither to "No user" nor to "Anchor required": a PERSON device in maintenance keeps its user, a LOCATION device its location. PENDING_ACCEPTANCE and RETURN_PENDING result only from actions — they are not in the manual status dropdown and are reached only via handover and return. IN_USE can be set manually (e.g. "end maintenance" for a device that kept its user); a new assignment, however, runs only via checkout, install or handover (see the rule below).
Anchor Rules
On every write (create, update, bulk plus checkout, checkin, install, deinstall, handover) the server validates the resulting state. The first rule violated is reported with HTTP 400 and the following error code:
| Rule | Condition | Error code |
| Consumable without user | CONSUMABLE ⟹ assignedToId == null | CONSUMABLE_NO_DIRECT_ASSIGNEE |
| Consumable status | CONSUMABLE ⟹ status ∈ {ORDERED, RECEIVED, AVAILABLE, RETIRED, DISPOSED} | CONSUMABLE_INVALID_STATUS |
| LOCATION without user | LOCATION ⟹ assignedToId == null | LOCATION_ASSET_CANNOT_HAVE_USER |
| Status without user | status in group "No user" ⟹ no user | ASSET_STATUS_ASSIGNED_CONFLICT |
| Anchor required | status ∈ {IN_USE, PENDING_ACCEPTANCE, RETURN_PENDING} ⟹ user and/or location | ASSET_STATUS_NEEDS_ANCHOR |
| PERSON strict | PERSON ∧ status ∈ {IN_USE, PENDING_ACCEPTANCE, RETURN_PENDING} ⟹ user (location is not enough) | PERSON_ASSET_NEEDS_USER |
| Note required | status = LOST ⟹ comment | ASSET_LOST_REQUIRES_NOTE |
Flow-only rule:
A PATCH /api/assets/:id must not change assignedToId in the same call as a transition to IN_USE → ASSET_ASSIGN_VIA_FLOW_ONLY. Assignment runs exclusively via checkout/handover/install. (An IN_USE→IN_USE owner change and status-only changes remain allowed.)
Transition Matrix
Unique items (PERSON/LOCATION):
| From | To |
ORDERED | RECEIVED, DISPOSED |
RECEIVED | AVAILABLE, MAINTENANCE, DISPOSED |
AVAILABLE | RESERVED, PENDING_ACCEPTANCE, IN_USE, MAINTENANCE, RETIRED, LOST, DISPOSED |
RESERVED | AVAILABLE, PENDING_ACCEPTANCE, IN_USE, LOST |
PENDING_ACCEPTANCE | IN_USE, AVAILABLE |
IN_USE | AVAILABLE, RETURN_PENDING, MAINTENANCE, RETIRED, LOST, DISPOSED |
MAINTENANCE | AVAILABLE, IN_USE, PENDING_ACCEPTANCE, RETURN_PENDING, RETIRED, LOST, DISPOSED |
RETURN_PENDING | AVAILABLE, MAINTENANCE, RETIRED, IN_USE |
RETIRED | AVAILABLE, DISPOSED |
LOST | AVAILABLE, DISPOSED |
DISPOSED | — (terminal) |
Consumables (CONSUMABLE) — reduced matrix:
| From | To |
ORDERED | RECEIVED, DISPOSED |
RECEIVED | AVAILABLE, DISPOSED |
AVAILABLE | RETIRED, DISPOSED |
RETIRED | AVAILABLE, DISPOSED |
DISPOSED | — (terminal) |
The Three Lifecycles
PERSON — anchor = user
- Direct checkout: → IN_USE + user, immediate, CHECKOUT_DIRECT record. External recipients: create user + email. No confirmation step.
- Handover with confirmation: → PENDING_ACCEPTANCE + recipient + email; accept → IN_USE; reject/cancel → revert to previous state.
- Return: Checkin / confirmReturn → AVAILABLE | MAINTENANCE | RETIRED, user cleared.
LOCATION — anchor = location (no handover)
- Install: → IN_USE + locationId (no user), deployedAt set. Direct IT action + activity log.
- Deinstall: → AVAILABLE | MAINTENANCE, locationId cleared (history in log), deployedAt cleared. Option keepLocation keeps the location.
CONSUMABLE — quantity
The status is not tied to an anchor: usually AVAILABLE, plus quantity and quantity assignments (ConsumableAssignment, to a user OR a location). Reduced status set, no handover, no CMDB relations.
Field Truth Table (what each action sets/clears)
| Action | status | assignedToId | locationId | deployedAt | Record |
| Direct checkout (PERSON) | IN_USE | → User | — | → now | CHECKOUT_DIRECT; email if external |
| Create handover | PENDING_ACCEPTANCE | → recipient | — | — | email to recipient |
| Handover accept | IN_USE | (stays) | — | → now | email to initiator |
| Install (LOCATION) | IN_USE | null | → location | → now | Activity |
| Checkin / confirmReturn | AVAILABLE|MAINTENANCE|RETIRED | → null | (stays) | → null | RETURN; email |
| Deinstall (LOCATION) | AVAILABLE|MAINTENANCE | null | → null | → null | Activity |
| Set maintenance | MAINTENANCE | unchanged | unchanged | unchanged | Activity (statusNote optional) |
| Report lost | LOST | → null | stays (last location) | → null | Activity (statusNote mandatory) |
| Retire / Dispose | RETIRED / DISPOSED | → null | stays | (retiredAt/disposedAt) | activity; blocked while active contract/license links exist |
Fields (API)
AssetType
| Field | Typ | Description |
trackingMode | enum (default PERSON) | PERSON | LOCATION | CONSUMABLE. Immutable once the type has assets (otherwise 409). |
standalone | boolean (default true) | false = built-in component (e.g. RAM/SSD): no standalone checkout/handover, follows its container via co-move. CONSUMABLE is always standalone. |
requiresConfirmation | boolean (default false) | Handover with recipient confirmation as the default for this type. |
hasTypePermissions | Boolean | Type lock: for assets of this type only the type-specific permissions apply (see permissions). |
Asset
| Field | Typ | Description |
statusNote | String? (Text) | Reason/comment for the current status. Mandatory on LOST, optional on RESERVED/MAINTENANCE; replaced/cleared on status change. Shown in the overview tab and coexists with the handover damageReport (separate facts). |
quantity | Int (Default 1) | Quantity — relevant only for CONSUMABLE types. |
deployedAt | DateTime? | Timestamp of deployment; cleared outside active states. |
Related Documentation
Endpoints & examples
Assets API — checkout/install/handover, relations, impact
Inventory
Inventory API — action model, missing/lost, auto-account