Equipment
Onboard equipment/systems tracked per vessel.
/equipmentCreate equipment
Registers a new piece of onboard equipment. Requires `equipmentType` and either `systemCategory` or `vesselSystemId` (the server resolves whichever one you omit from the other when possible). Attempts an automatic catalog-model link on create and returns any suggested manufacturer maintenance kits for the matched model.
| Field | Type | Required | Description |
|---|---|---|---|
| vesselId | string (uuid) | required | — |
| equipmentType | string | required | — |
| systemCategory | string | optional | — |
| vesselSystemId | string (uuid) | optional | — |
| make | string | optional | — |
| model | string | optional | — |
| serialNumber | string | optional | — |
| installDate | string (date-time) | optional | — |
| hoursAtInstall | integer | optional | — |
| currentHours | integer | optional | — |
| capacity | object | optional | — |
| locationOnVessel | string | optional | — |
| warrantyExpires | string (date-time) | optional | — |
| notes | string | optional | — |
| Field | Type | Required | Description |
|---|---|---|---|
| data | any | required | — |
curl -sS -X POST "https://api.owlmar.com/v1/equipment" \-H "Authorization: Bearer $OWLMAR_API_KEY" \-H "Content-Type: application/json" \-d '{"vesselId": "'"$VESSEL_ID"'","systemCategory": "propulsion","equipmentType": "Main Engine — Port","make": "MAN","model": "D2862 LE466"}'
const res = await fetch('https://api.owlmar.com/v1/equipment', {method: 'POST',headers: {Authorization: `Bearer ${process.env.OWLMAR_API_KEY}`,'Content-Type': 'application/json',},body: JSON.stringify({vesselId,systemCategory: 'propulsion',equipmentType: 'Main Engine — Port',make: 'MAN',model: 'D2862 LE466',}),});const body = await res.json();if (!res.ok) {// Switch on body.code, never on body.error (wording may change without notice).switch (body.code) {case 'VALIDATION_ERROR':case 'VALIDATION_FAILED':throw new Error(`bad request: ${JSON.stringify(body.meta ?? body.error)}`);case 'INSUFFICIENT_SCOPE':throw new Error('this API key is not scoped for equipment_tracking:full');case 'VESSEL_OUT_OF_SCOPE':throw new Error('this API key is not scoped to that vessel');case 'RATE_LIMITED':throw new Error(`rate limited, retry after ${body.meta?.retryAfterMs}ms`);default:throw new Error(`unexpected error: ${body.code} — ${body.error}`);}}const { data: equipment } = body;
resp = requests.post("https://api.owlmar.com/v1/equipment",headers={"Authorization": f"Bearer {OWLMAR_API_KEY}"},json={"vesselId": vessel_id,"systemCategory": "propulsion","equipmentType": "Main Engine — Port","make": "MAN","model": "D2862 LE466",},)resp.raise_for_status()equipment = resp.json()["data"]
/equipment/{id}Get equipment
Returns a single equipment record with its 10 most recent maintenance events, documents, and catalog model info.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string (uuid) | required | — |
| Field | Type | Required | Description |
|---|---|---|---|
| data | any | required | — |
/equipment/{id}Update equipment
Partial update. Any writable `Equipment` field may be included; only the fields present in the body are changed. Setting `currentHours` server-stamps `currentHoursAt` and re-evaluates any hours-based maintenance schedules, returning `dueSchedules` for anything that crossed into due/due-soon as a result.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string (uuid) | required | — |
| Field | Type | Required | Description |
|---|---|---|---|
| systemCategory | string | optional | — |
| vesselSystemId | string (uuid) | null | optional | — |
| equipmentType | string | optional | — |
| make | string | null | optional | — |
| model | string | null | optional | — |
| serialNumber | string | null | optional | — |
| installDate | string (date-time) | null | optional | — |
| hoursAtInstall | integer | null | optional | — |
| currentHours | integer | null | optional | — |
| capacity | object | null | optional | — |
| locationOnVessel | string | null | optional | — |
| warrantyExpires | string (date-time) | null | optional | — |
| notes | string | null | optional | — |
| Field | Type | Required | Description |
|---|---|---|---|
| data | any | required | — |
curl -sS -X PATCH "https://api.owlmar.com/v1/equipment/$EQUIPMENT_ID" \-H "Authorization: Bearer $OWLMAR_API_KEY" \-H "Content-Type: application/json" \-d '{"currentHours": 4821}'
const res = await fetch(`https://api.owlmar.com/v1/equipment/${equipmentId}`, {method: 'PATCH',headers: {Authorization: `Bearer ${process.env.OWLMAR_API_KEY}`,'Content-Type': 'application/json',},body: JSON.stringify({ currentHours: 4821 }),});const { data: equipment } = await res.json();
resp = requests.patch(f"https://api.owlmar.com/v1/equipment/{equipment_id}",headers={"Authorization": f"Bearer {OWLMAR_API_KEY}"},json={"currentHours": 4821},)equipment = resp.json()["data"]
/equipment/vessel/{vesselId}List a vessel's equipment
Returns non-archived equipment for a vessel, each annotated with `hoursStatus` (hours-based service-due computation). Supports opaque cursor pagination via `?cursor=` — omit `cursor` entirely for the first page, then pass back the `cursor` value from `pagination.cursor` for subsequent pages until `pagination.hasMore` is `false`. Default sort without `?cursor=` is by `systemCategory`; the cursor-paginated path always sorts `(createdAt DESC, id DESC)`.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| vesselId | path | string (uuid) | required | — |
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| cursor | query | string | optional | Opaque pagination cursor from a previous response's `pagination.cursor`. Omit this param entirely for the first page of cursor mode; pass `cursor=` (empty) is also accepted as "start from the beginning in cursor mode." Omitting `cursor` altogether (not even as an empty string) falls back to legacy `?page=` semantics on endpoints that still support it. |
| limit | query | integer | optional | — |
| Field | Type | Required | Description |
|---|---|---|---|
| data | array<object> | required | — |
| data[].id | string (uuid) | optional | — |
| data[].vesselId | string (uuid) | optional | — |
| data[].systemCategory | string | optional | — |
| data[].vesselSystemId | string (uuid) | null | optional | — |
| data[].vesselSystem | object | null | optional | — |
| data[].vesselSystem.id | string (uuid) | optional | — |
| data[].vesselSystem.systemKey | string | optional | — |
| data[].vesselSystem.name | string | optional | — |
| data[].equipmentType | string | optional | — |
| data[].make | string | null | optional | — |
| data[].model | string | null | optional | — |
| data[].serialNumber | string | null | optional | — |
| data[].installDate | string (date-time) | null | optional | — |
| data[].hoursAtInstall | integer | null | optional | — |
| data[].currentHours | integer | null | optional | — |
| data[].currentHoursAt | string (date-time) | null | optional | — |
| data[].capacity | object | null | optional | — |
| data[].locationOnVessel | string | null | optional | — |
| data[].warrantyExpires | string (date-time) | null | optional | — |
| data[].photos | array<string> | optional | — |
| data[].notes | string | null | optional | — |
| data[].equipmentModelId | string (uuid) | null | optional | — |
| data[].equipmentModel | object | null | optional | — |
| data[].equipmentModel.id | string (uuid) | optional | — |
| data[].equipmentModel.modelName | string | null | optional | — |
| data[].equipmentModel.manualUrl | string | null | optional | — |
| data[].equipmentModel.manualContentHash | string | null | optional | — |
| data[].equipmentModel.manualDiscoveryStatus | string | null | optional | — |
| data[].equipmentModel.manualDiscoverySource | string | null | optional | — |
| data[].equipmentModel.maintenanceSchedule | object | null | optional | — |
| data[].equipmentModel.manufacturer | object | optional | — |
| data[].equipmentModel.maintenanceKits | array<object> | optional | — |
| data[].hoursStatus | array<object> | optional | Derived hours-based service-due status per applicable maintenance kit/schedule. |
| data[].archivedAt | string (date-time) | null | optional | — |
| data[].createdAt | string (date-time) | optional | — |
| data[].updatedAt | string (date-time) | optional | — |
| pagination | object | optional | Canonical cursor-pagination block. On endpoints not yet retrofitted for true cursor support, `cursor` is a best-effort display-only value (not decodable, no `?cursor=` branch accepts it back) — see each endpoint's description for whether it has full cursor support. |
| pagination.cursor | string | null | optional | Opaque token for the next page, or `null` when `hasMore` is `false`. |
| pagination.limit | integer | required | — |
| pagination.hasMore | boolean | required | — |
| pagination.total | integer | required | Total row count matching the filter (not just this page). |
curl -sS "https://api.owlmar.com/v1/equipment/vessel/$VESSEL_ID?cursor=&limit=25" \-H "Authorization: Bearer $OWLMAR_API_KEY"
async function listAllEquipment(vesselId) {const items = [];// Empty string on the FIRST call is intentional — it opts into cursor mode// without seeking a specific starting point. Omitting `cursor` entirely falls// back to legacy ?page= behavior instead.let cursor = '';while (true) {const url = new URL(`https://api.owlmar.com/v1/equipment/vessel/${vesselId}`);url.searchParams.set('cursor', cursor);url.searchParams.set('limit', '100');const res = await fetch(url, {headers: { Authorization: `Bearer ${process.env.OWLMAR_API_KEY}` },});const { data, pagination } = await res.json();items.push(...data);if (!pagination.hasMore) break;cursor = pagination.cursor;}return items;}
def list_all_equipment(vessel_id):items = []cursor = "" # opt into cursor mode on the first callwhile True:resp = requests.get(f"https://api.owlmar.com/v1/equipment/vessel/{vessel_id}",headers={"Authorization": f"Bearer {OWLMAR_API_KEY}"},params={"cursor": cursor, "limit": 100},)resp.raise_for_status()body = resp.json()items.extend(body["data"])if not body["pagination"]["hasMore"]:breakcursor = body["pagination"]["cursor"]return items
