Skip to main content

Asset servicing

Servicing is how Loca.fi tracks maintenance work performed on tracked assets — an inspection, calibration, repair, clean, or a delivery/pickup job. You define what can be serviced once (a service definition), then record each visit as a service record with the actual items scanned against it.

Retail vs Asset

Servicing is an asset-management feature. It exists for tenants that track individual assets (equipment, tools, returnable containers) through maintenance and delivery cycles. Retail tenants typically don't use the Services domain at all.

Read Service records first for the Open → InProgress → Complete/Cancelled state machine — this guide is the doing, that page is the understanding.

Overview

Three entities, one flow:

  1. Service type — a category ({Type, SystemType}). Six system types are auto-seeded per organisation (DeliverGoods, PickupGoods, DeliverSku, PickupSku, DeliverItem, PickupItem); create custom ones with createservicetype.
  2. Service definition (ServiceMaster) — the catalogue entry, "a SKU for a job": a ServiceCode, a service type, and optionally the specific item or SKU it applies to — createservice.
  3. Service record — one performed (or scheduled) visit against a definition, with service items (the assets involved) attached — createservicerecord.

Definitions and types share the ServiceMaster permission module; records and their item lines are gated on ServiceRecord.

There is no interval/recurrence field on a service definition — cadence is expressed per record via ScheduledDate, and recurring due/overdue tracking is the event-schedule feature (see Notifications & alerts).

Prerequisites

  • A token with ServiceMaster and ServiceRecord permissions — see Getting started.
  • Items with tags already onboarded (or let the record auto-create them — step 4).
  • Optional: service extended properties. Task/service custom fields are template-less — create them with TemplateType: "Service" and attach values directly; see Templates & extended properties.

Step-by-step

1. Pick or create a service type

List what's already there (the six seeded system types plus any custom) with getfilteredservicetypes, or create a custom category:

curl -sk -X POST "http://<host>/api/servicetypes/createservicetype" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Type":"Annual inspection"}'

Custom types are always created SystemType: false. Pick a system Deliver/Pickup type instead when completing the record should move the serviced items to a destination place (step 5).

2. Define the service (ServiceMaster)

curl -sk -X POST "http://<host>/api/services/createservice" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"ServiceCode":"INSP-FORKLIFT","Description":"Annual forklift inspection",
"ServiceTypeId":"<serviceTypeId>","SkuId":"<forkliftSkuId>"}'

Returns 200 with a ServiceMasterSummaryDto — capture the Id. ItemId and SkuId are optional and mutually exclusive: set ItemId to scope the definition to one specific asset, SkuId to a whole asset type, or neither for a general job. System types constrain the link — DeliverItem/PickupItem require an ItemId, DeliverSku/PickupSku a SkuId.

Manage definitions with getfilteredservices (OData), getservice/{id}, updateservice (full-replace), and deleteservice/{id}; a lean picker list is at get-filtered-services-for-lookup.

3. Open a service record for the visit

curl -sk -X POST "http://<host>/api/servicerecords/createservicerecord" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"ServiceNumber":"SRV-2026-0042","ServiceMasterId":"<serviceMasterId>",
"RequestedQty":3,"ScheduledDate":"2026-07-21T09:00:00Z",
"Notes":"July inspection round","MoveItemsOnComplete":false}'

Returns 200 with a ServiceRecordDetailDto in status "Open" — unless you supplied ServiceItems inline, in which case it starts "InProgress". Notable fields:

  • ServiceNumber — unique among non-terminal records; completing or cancelling a record frees its number for reuse.
  • FromPlaceId / ToPlaceId — the movement leg. ToPlaceId is required when the service type is a system Deliver/Pickup type or MoveItemsOnComplete is true.
  • RequestedQty / AdjustedQty / ManualQty — planned/corrected counts (must be ≥ 0). ScannedQty is server-maintained from the item lines — don't try to set it.
  • ReasonId — must reference a reason with the Service scope.
  • ScheduledDate — when the visit is due; clipped to whole seconds.

4. Attach the serviced assets (service items)

As the technician scans tags (or you know the item ids), add lines with createserviceitem:

curl -sk -X POST "http://<host>/api/serviceitems/createserviceitem" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"ServiceRecordId":"<recordId>","AutoCreateItems":true,
"CreatePlaceId":"<workshopPlaceId>",
"ServiceItems":[
{"TagNumber":"E280116060000209DC5A0AB1","Notes":"Brake pads replaced"},
{"ItemId":"<knownItemId>","Attribute1":"Pass"}]}'

Each line resolves by ItemId or TagNumber. An unknown tag is auto-created as a new item at CreatePlaceId (default: the organisation's root place) when AutoCreateItems is true (the default); with AutoCreateItems: false an unresolved tag is silently skipped — no line, no error. Every add/delete bumps the record's ScannedQty and any mutation bumps an Open record to InProgress.

Per-line housekeeping: updateserviceitem (notes, attributes, extended properties — not the item/tag reference), deleteserviceitem/{id}, bulk deleteserviceitems (partial-success: bad lines accumulate errors while good lines delete). Photos as proof-of-service attach via the record's uploadmedia or the line-level equivalent.

Raw scan capture without item resolution is also available — updateservicerecordtags adds/removes plain tag-number scans (ServiceRecordTags, de-duplicated), separate from the resolved item lines.

5. Complete — or cancel — the record

curl -sk -X POST "http://<host>/api/servicerecords/completeservicerecord" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Id":"<recordId>","CompleteTime":"2026-07-21T11:30:00Z"}'

Sets Status: "Complete" and stamps CompletedOn (omit CompleteTime to use the server's current time). If MoveItemsOnComplete is true — or the service type is a system Deliver/Pickup type — completion moves every attached item to ToPlaceId, writing movement history and setting item state to Moved (see Item lifecycle). Completion also auto-completes a linked system-type task action — see Tasks & schedules for driving servicing through assigned tasks.

cancelservicerecord abandons the visit with no item movement. Both terminal states lock the record against further edits (re-issuing the same transition is an idempotent 200 no-op); only deleteservicerecord/{id} works from any state. Full matrix: Service records.

Query history and due-for-service

Recordsgetfilteredservicerecords is the OData list (ServiceRecordSummaryDto: status, quantities, places, ScheduledDate, CompletedOn). Due-for-service is a filter over open records past their scheduled date:

curl -sk "http://<host>/api/servicerecords/getfilteredservicerecords?%24filter=Status%20eq%20%27Open%27%20and%20ScheduledDate%20le%202026-07-07T00:00:00Z" \
-H "Authorization: Token $TOKEN"

Per-asset historygetfiltereditemservicerecords returns an item-centric view (one row per item × record, ItemServiceRecordSummaryDto = the record fields plus ItemId/ItemName/ItemTagNumber/ItemAssetNumber and SKU columns). Filter %24filter=ItemId eq <guid> for one asset's full service history.

Item linesgetfilteredserviceitems lists lines across records; getserviceitem/{id} returns one with its extended properties and media.

For recurring cadence ("every asset must be cleaned monthly") and overdue alerting, use event schedules and monitors rather than polling — Notifications & alerts.

Reporting and CSV export

Error handling

All failures follow the platform-wide error and response conventions400 with a JSON array of { ErrorCode, Message, Field }.

SituationResponse
Missing ServiceCode (definition) or ServiceMasterId (record)400 MissingRequiredField
Unknown ServiceTypeId, ServiceMasterId, FromPlaceId, ToPlaceId, or non-Service-scope ReasonId400 InvalidReference
Both ItemId and SkuId set on a definition — or the one a system type requires missing400 InvalidReference "Item or SKU value needs to be provided"
ServiceNumber already used by a non-terminal record400 DuplicateResource
Missing ToPlaceId with MoveItemsOnComplete or a system Deliver/Pickup type400 InvalidValue "ToPlaceId"
Negative quantity400 InvalidValue
Mutating a Complete/Cancelled record (update, tags, items, media)400 InvalidOperation "Status" "Record cannot be updated"
CompleteCancel (or vice versa)400 InvalidOperation "Status" — terminal states can't cross
Get record by unknown id400 "Service record not found" (ErrorCode collapses to InvalidOperation; the Message carries the signal)
Service-item line resolving to a different record than ServiceRecordId400 InvalidReference (bulk delete: accumulated per line, others still succeed)
No token / wrong scheme (Bearer)401