The machines that your crews can record when they clock in. Each piece of equipment has a
code that is unique in its company, a status and an optional assignment to a Job, Work
Order or Meet Point.
status | Meaning |
|---|---|
A | Active. Only A equipment can be picked on the phone. |
D | Down |
I | Inactive |
A record that you read back can carry another status code that its owner sent.
Scopes: equipment:read to read, equipment:write to write. Your key can write only
equipment that its own source system owns. A write to another owner's equipment gets
403 not_owner.
Back to the index · Conventions · Errors
Every endpoint can also return 401 (no key, or a key that is not valid, revoked or
expired) and 503 (service_unavailable or instance_mismatch). The tables below list
only the other statuses.
The Equipment object
| Field | Type | Description |
|---|---|---|
equipmentId | integer | The equipment's id |
companyId | integer | The company's id |
companyCode | string | The company |
divisionCode | string or null | The division. It cannot change. |
code | string | The equipment number. Unique in the company, compared without case. It cannot change. |
description | string or null | The effective description: an administrator's override where one is set |
status | string | The effective status: A, D or I |
ownership | string or null | owned or rented (effective value) |
assignedActivity | object or null | The activity that the machine is on now, or null |
assignedActivity.activityId | integer | The activity's id |
assignedActivity.code | string | The activity's code |
assignedActivity.name | string or null | The activity's name |
assignedActivity.kind | string | Job, WorkOrder or MeetPoint |
assignedActivity.isActive | boolean | Whether the activity is active |
isActive | boolean | False after the owner deactivates it |
isAvailable | boolean | True when isActive is true and the effective status is A. Only then can a crew pick the machine and clock in with it. |
sourceSystem | string | The owner. It never changes. |
isLocallyOverridden | boolean | True when an administrator overrides a field |
overriddenFields | array of strings | The fields that an override covers |
source.description | string or null | The owner's own description, under any override |
source.status | string | The owner's own status |
source.ownership | string or null | The owner's own ownership |
source.assignedActivityId | integer or null | The owner's own assignment |
source.sourceJob | string or null | The raw job code from the owner's system, when a sync owns the equipment. Null otherwise. |
lastLocation | object or null | The last known position, when the owner reports one |
lastLocation.latitude | number | Latitude |
lastLocation.longitude | number | Longitude |
lastLocation.seenAt | string or null | When the owner says the machine was seen (UTC) |
syncedAt | string or null | When a sync last wrote the record. Null for equipment that no sync owns. |
createdAt | string | Server time |
updatedAt | string | Server time. Send it back as expectedUpdatedAt. |
GET /equipment-catalog
Lists equipment, ordered by code. Filters use the effective values.
Scope: equipment:read
Query parameters
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
q | string | No | First 100 characters are used | Search the code and the description. Case does not matter. | |
companyCode | string | No | Keep only this company | ||
divisionCode | string | No | Keep only this division | ||
status | string | No | A, D, I | Keep only this status | |
ownership | string | No | owned, rented | Keep only this ownership | |
sourceSystem | string | No | Keep only this owner, for example your own source system | ||
assignedActivityId | integer | No | 1 or more | Keep only machines on this activity | |
unassigned | string | No | true, false | true keeps machines on no activity. false keeps machines on one. Not with assignedActivityId. | |
isActive | string | No | true, false | Keep only active or only inactive machines | |
isAvailable | string | No | true, false | Keep only machines that a crew can or cannot pick | |
isLocallyOverridden | string | No | true, false | Keep only machines with or without an administrator's override | |
f_code | string | No | Max 200 characters | A pattern filter on the code | |
f_description | string | No | Max 200 characters | A pattern filter on the description. Equipment with no description matches only a negation. | |
f_divisionCode | string | No | Max 200 characters | A pattern filter on the division code | |
f_assignedActivity | string | No | Max 200 characters | A pattern filter on the assigned activity's code, a space and its name. Not assigned: no text. | |
f_syncedAt | string | No | Max 200 characters | A day filter on syncedAt. See pattern filters. | |
limit | integer | No | 50 | 1 to 200 | Page size |
offset | integer | No | 0 | 0 or more | Rows to skip |
Pattern filters
The f_ parameters narrow the list with a short pattern. Each one is added to every other
filter (AND). Case does not matter. A pattern has at most 200 characters and 25 terms. A
pattern that is too long or not valid gets 400.
| Term | Matches |
|---|---|
text | Values that contain text |
=text | The value text exactly |
!text | Values that do not contain text |
^text | Values that start with text |
!^text | Values that do not start with text |
!! | Any value that is not empty |
a*b | A glob from the start to the end |
>n, <n | Values above or below a number |
a-b | A number range |
a,b | a or b |
a&b | a and b |
!(a&b) | Not the group in brackets |
An empty value matches only a negation, so !! keeps equipment that has a value.
f_syncedAt takes days as m/d/yyyy only: =6/1/2026, >=6/1/2026, >6/1/2026,
<=6/30/2026, <6/30/2026, the range 6/1/2026-6/30/2026 (both days included) and !!
(synced at least once). Combine them with ,, & and !( ). Any other term gets 400.
Days are counted in the service's default site time zone.
Example: f_code=^EX&f_description=excavator%26!mini.
Example request
curl "$TIMECARD_BASE_URL/equipment-catalog?companyCode=ACME&sourceSystem=acme-erp&limit=200" \
-H "X-API-Key: $TIMECARD_API_KEY"
Example response 200 OK
{
"status": 200,
"message": "Success: equipment retrieved.",
"record": {
"total": 1,
"records": [
{
"equipmentId": 3051,
"companyId": 1,
"companyCode": "ACME",
"divisionCode": "PAVING",
"code": "EX-210",
"description": "CAT 320 excavator",
"status": "A",
"ownership": "owned",
"assignedActivity": {
"activityId": 48213,
"code": "J-2026-104",
"name": "Riverside Bridge Widening",
"kind": "Job",
"isActive": true
},
"isActive": true,
"isAvailable": true,
"sourceSystem": "acme-erp",
"isLocallyOverridden": false,
"overriddenFields": [],
"source": {
"description": "CAT 320 excavator",
"status": "A",
"ownership": "owned",
"assignedActivityId": 48213,
"sourceJob": null
},
"lastLocation": {
"latitude": 30.2672,
"longitude": -97.7431,
"seenAt": "2026-11-03T14:55:00.000Z"
},
"syncedAt": null,
"createdAt": "2026-11-03T15:00:12.000Z",
"updatedAt": "2026-11-03T15:00:12.000Z"
}
]
}
}
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | A filter is not valid. detail names the parameter. |
| 403 | insufficient_scope | The key does not have equipment:read |
| 500 | internal_error | The equipment could not be read |
GET /equipment-catalog/{equipmentId}
Returns one piece of equipment.
Scope: equipment:read
Path parameters
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
equipmentId | integer | Yes | 1 or more | The equipment's id |
Example request
curl "$TIMECARD_BASE_URL/equipment-catalog/3051" \
-H "X-API-Key: $TIMECARD_API_KEY"
Example response 200 OK
{
"status": 200,
"message": "Success: equipment retrieved.",
"record": {
"equipmentId": 3051,
"companyCode": "ACME",
"code": "EX-210",
"status": "A",
"isAvailable": true,
"sourceSystem": "acme-erp",
"updatedAt": "2026-11-03T15:00:12.000Z"
}
}
The record has every field of the Equipment object. It is
shortened here.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | equipmentId is not a positive integer |
| 403 | insufficient_scope | The key does not have equipment:read |
| 404 | not_found | No equipment has this id within the key's companies |
| 500 | internal_error | The equipment could not be read |
POST /equipment-catalog
Creates one piece of equipment, owned by your key's source system. The owner never changes.
Scope: equipment:write
Request body
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
code | string | Yes | 1 to 30 characters | The equipment number. Trimmed, case kept. Must be free in the company, under any owner or division, active or not. | |
companyCode | string | When the key covers more than one company | Max 10 characters | The company | |
divisionCode | string or null | No | 1 to 20 characters | The division. It cannot change later. | |
description | string or null | No | 1 to 200 characters | The description | |
status | string | No | A | A, D, I | Active, Down or Inactive |
ownership | string or null | No | owned, rented | Owned or rented | |
assignedActivityId | integer or null | No | 1 or more | The Job, Work Order or Meet Point of the same company that the machine is on, active or not. Send a JSON number, not a string. Null means none. | |
lastLocation | object or null | No | The last known position | ||
lastLocation.latitude | number | Yes | -90 to 90 | Latitude | |
lastLocation.longitude | number | Yes | -180 to 180 | Longitude | |
lastLocation.seenAt | string or null | No | ISO 8601 date-time | When the machine was seen (UTC) |
Example request
curl -X POST "$TIMECARD_BASE_URL/equipment-catalog" \
-H "X-API-Key: $TIMECARD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"companyCode": "ACME",
"code": "EX-210",
"description": "CAT 320 excavator",
"divisionCode": "PAVING",
"status": "A",
"ownership": "owned",
"assignedActivityId": 48213,
"lastLocation": { "latitude": 30.2672, "longitude": -97.7431, "seenAt": "2026-11-03T14:55:00Z" }
}'
Example response 201 Created
{
"status": 201,
"message": "Success: equipment created.",
"record": {
"equipmentId": 3051,
"companyCode": "ACME",
"divisionCode": "PAVING",
"code": "EX-210",
"description": "CAT 320 excavator",
"status": "A",
"ownership": "owned",
"isActive": true,
"isAvailable": true,
"sourceSystem": "acme-erp",
"updatedAt": "2026-11-03T15:00:12.000Z"
}
}
The record has every field of the Equipment object. It is
shortened here.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | The body is not valid JSON or breaks a rule, companyCode is missing when the key covers several companies, or assignedActivityId is not an activity of the company |
| 403 | insufficient_scope | The key does not have equipment:write |
| 403 | forbidden | A key bound to one company names another, or the key has no source system |
| 404 | not_found | companyCode names no active company that the key can reach |
| 409 | code_taken | The company already has this equipment number, under any owner or division, active or not. Update or reactivate that record instead. |
| 500 | internal_error | The equipment could not be created. The record can still exist. Look it up by its code before you retry. |
PATCH /equipment-catalog/{equipmentId}
Changes only the fields that you send. code, divisionCode and companyCode cannot
change.
Scope: equipment:write
Path parameters
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
equipmentId | integer | Yes | 1 or more | The equipment's id |
Request body
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
expectedUpdatedAt | string | No (recommended) | ISO 8601 date-time with Z or an offset | The updatedAt that you last read. If the equipment changed after that, you get 409 and nothing is saved. | |
description | string or null | No | 1 to 200 characters | The description | |
status | string | No | A, D, I | Active, Down or Inactive | |
ownership | string or null | No | owned, rented | Owned or rented | |
assignedActivityId | integer or null | No | 1 or more | The activity of the same company that the machine is on. Send a JSON number. Null removes the assignment. | |
lastLocation | object or null | No | The last known position | ||
lastLocation.latitude | number | Yes | -90 to 90 | Latitude | |
lastLocation.longitude | number | Yes | -180 to 180 | Longitude | |
lastLocation.seenAt | string or null | No | ISO 8601 date-time | When the machine was seen (UTC) |
Example request
curl -X PATCH "$TIMECARD_BASE_URL/equipment-catalog/3051" \
-H "X-API-Key: $TIMECARD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expectedUpdatedAt": "2026-11-03T15:00:12.000Z",
"status": "D",
"assignedActivityId": null
}'
Example response 200 OK
{
"status": 200,
"message": "Success: equipment updated.",
"record": {
"equipmentId": 3051,
"code": "EX-210",
"status": "D",
"assignedActivity": null,
"isAvailable": false,
"updatedAt": "2026-11-03T15:30:44.000Z"
}
}
The record has every field of the Equipment object. It is
shortened here. When the values you send are already stored, nothing changes and the
message is Success: nothing to change.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | equipmentId is not a positive integer, the body is not valid JSON, names an unknown field or one that cannot change, or a value breaks its rule, or assignedActivityId is not an activity of the same company |
| 403 | insufficient_scope | The key does not have equipment:write |
| 403 | forbidden | The key has no source system |
| 403 | not_owner | Another source system owns this equipment |
| 404 | not_found | No equipment has this id within the key's companies |
| 409 | conflict | expectedUpdatedAt does not match (the equipment changed after you read it), or the record kept changing during the write. Read it again and retry. |
| 500 | internal_error | The equipment could not be written |
DELETE /equipment-catalog/{equipmentId}
Deactivates the equipment. Nothing is deleted. Crews can no longer pick it, and a clock-in
that names it is refused. Undo it with
POST /equipment-catalog/{equipmentId}/reactivate.
Scope: equipment:write
Path parameters
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
equipmentId | integer | Yes | 1 or more | The equipment's id |
No request body.
Example request
curl -X DELETE "$TIMECARD_BASE_URL/equipment-catalog/3051" \
-H "X-API-Key: $TIMECARD_API_KEY"
Example response 200 OK
{
"status": 200,
"message": "Success: equipment deactivated.",
"record": {
"equipmentId": 3051,
"code": "EX-210",
"isActive": false,
"isAvailable": false,
"updatedAt": "2026-11-03T15:41:02.000Z"
}
}
The record has every field of the Equipment object. It is
shortened here. When the equipment is already inactive, the message is
Success: nothing to change.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | equipmentId is not a positive integer |
| 403 | insufficient_scope | The key does not have equipment:write |
| 403 | forbidden | The key has no source system |
| 403 | not_owner | Another source system owns this equipment |
| 404 | not_found | No equipment has this id within the key's companies |
| 409 | conflict | The record kept changing during the write. Read it again and retry. |
| 500 | internal_error | The equipment could not be written |
POST /equipment-catalog/{equipmentId}/reactivate
Undoes a deactivation. The equipment is active again.
Scope: equipment:write
Path parameters
| Name | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
equipmentId | integer | Yes | 1 or more | The equipment's id |
No request body.
Example request
curl -X POST "$TIMECARD_BASE_URL/equipment-catalog/3051/reactivate" \
-H "X-API-Key: $TIMECARD_API_KEY"
Example response 200 OK
{
"status": 200,
"message": "Success: equipment reactivated.",
"record": {
"equipmentId": 3051,
"code": "EX-210",
"status": "D",
"isActive": true,
"isAvailable": false,
"updatedAt": "2026-11-03T15:45:19.000Z"
}
}
isAvailable is true again only when the status is A. The record has every field of
the Equipment object. It is shortened here.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_failed | equipmentId is not a positive integer |
| 403 | insufficient_scope | The key does not have equipment:write |
| 403 | forbidden | The key has no source system |
| 403 | not_owner | Another source system owns this equipment |
| 404 | not_found | No equipment has this id within the key's companies |
| 409 | conflict | The record kept changing during the write. Read it again and retry. |
| 500 | internal_error | The equipment could not be written |