> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sensorup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connected Asset Management Guide

> Working with asset types, properties, and asset hierarchy in the SensorUp Connected Asset Management (CAM) system.

# Connected Asset Management Guide

The Connected Asset Management (CAM) API provides comprehensive asset management capabilities for connected operations through the `su-cam-assets` subgraph. CAM handles asset types, asset properties, asset hierarchy, and geospatial operations.

## Overview

CAM enables you to:

* Define and manage **asset types** with custom property schemas
* Create and organize **assets** with hierarchical relationships
* Query assets by type, location, and properties
* Perform batch operations for large-scale asset management
* Track asset states and status changes
* Manage geospatial data and relationships

## Core Concepts

### Asset Types

Asset types define the schema and structure for assets. Each asset type specifies:

* **Properties**: Custom attributes and their data types
* **Hierarchy**: Parent-child relationships between assets
* **Extensions**: External data source integrations
* **Status tracking**: Asset state management configuration

### Assets

Assets are instances of asset types representing physical or logical entities in your connected operations:

* **Sites**: Top-level geographic locations
* **Equipment**: Major operational assets
* **Components**: Sub-components within equipment
* **Wells**: Oil and gas well assets
* **Pipelines**: Pipeline infrastructure
* **Unattributed**: Assets not yet assigned to a specific type

### Asset Hierarchy

Assets can be organized in parent-child relationships, enabling:

* Multi-level organizational structures
* Inherited properties and configurations
* Hierarchical queries and filtering
* Geographic nesting (e.g., equipment within sites)

## Querying Assets

### Get All Asset Types

```graphql theme={null}
query GetAssetTypes {
  assetTypes {
    all(first: 20) {
      edges {
        node {
          assetTypeId
          name
          description
          properties {
            id
            name
            dataType
            required
          }
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}
```

### Get Specific Asset Type

```graphql theme={null}
query GetAssetType($assetTypeId: ID!) {
  assetTypes {
    byId(assetTypeId: $assetTypeId) {
      assetTypeId
      name
      description
      properties {
        id
        name
        dataType
        required
        defaultValue
      }
      extensions {
        id
        name
        connectionDetails {
          ... on AssetTypeExtensionConnectionSimple {
            idProperty
            idColumn
            stateProperty
          }
        }
      }
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "assetTypeId": "site"
}
```

### Query Assets by Type

```graphql theme={null}
query GetAssetsByType($assetTypeId: ID!) {
  camAssets(assetTypeId: $assetTypeId) {
    all(first: 20) {
      edges {
        node {
          id
          name
          description
          assetTypeId
          active
          geometry {
            type
            coordinates
          }
          properties
          parent {
            id
            name
          }
          children {
            id
            name
            assetTypeId
          }
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "assetTypeId": "equipment"
}
```

### Query Asset by ID

```graphql theme={null}
query GetAsset($id: ID!) {
  camAssets {
    byId(id: $id) {
      id
      name
      description
      assetTypeId
      active
      geometry {
        type
        coordinates
      }
      properties
      parent {
        id
        name
        assetTypeId
      }
      status {
        statusId
        label
        setAt
        setBy
      }
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "id": "site-001"
}
```

### Filter and Search Assets

```graphql theme={null}
query SearchAssets($assetTypeId: ID!, $searchText: String!) {
  camAssets(assetTypeId: $assetTypeId) {
    all(
      first: 20
      fullTextSearch: { text: $searchText }
      filter: { active: true }
    ) {
      edges {
        node {
          id
          name
          description
          assetTypeId
        }
      }
      totalCount
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "assetTypeId": "site",
  "searchText": "compressor"
}
```

## Creating and Updating Assets

### Create an Asset

```graphql theme={null}
mutation CreateAsset($input: CreateAssetInput!) {
  createAsset(input: $input) {
    asset {
      id
      name
      assetTypeId
      properties
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetId": "site-abc",
    "name": "Compressor Station ABC",
    "description": "Primary compression facility",
    "assetTypeId": "site",
    "active": true,
    "group": "your-tenant-id",
    "geometry": {
      "type": "Point",
      "coordinates": [-114.0719, 51.0447]
    },
    "propertyValues": [
      {
        "propertyId": "operator",
        "value": "ACME Energy"
      },
      {
        "propertyId": "capacity",
        "value": "5000"
      }
    ]
  }
}
```

### Update Asset Properties

```graphql theme={null}
mutation UpdateAssetProperty($input: UpdateAssetPropertyInput!) {
  updateAssetProperty(input: $input) {
    asset {
      id
      properties
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetId": "site-abc",
    "group": "your-tenant-id",
    "propertyValues": [
      {
        "propertyId": "capacity",
        "value": "6000"
      }
    ]
  }
}
```

### Update Asset Geometry

```graphql theme={null}
mutation UpdateAssetGeometry($input: UpdateAssetGeometryInput!) {
  updateAssetGeometry(input: $input) {
    asset {
      id
      geometry {
        type
        coordinates
      }
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetId": "site-abc",
    "group": "your-tenant-id",
    "geometry": {
      "type": "Polygon",
      "coordinates": [[
        [-114.073, 51.045],
        [-114.071, 51.045],
        [-114.071, 51.044],
        [-114.073, 51.044],
        [-114.073, 51.045]
      ]]
    }
  }
}
```

### Create Asset Status Record

```graphql theme={null}
mutation CreateAssetStatus($input: CreateAssetStatusInput!) {
  createAssetStatus(input: $input) {
    assetStatus {
      assetId
      statusId
      label
      setAt
      setBy
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetId": "equipment-001",
    "group": "your-tenant-id",
    "statusId": "operational",
    "label": "Fully Operational",
    "notes": "Post-maintenance verification complete"
  }
}
```

## Batch Operations

For large-scale asset creation or updates, CAM provides batch upsert capabilities.

### Generate Batch Upload URL

```graphql theme={null}
query GenerateUploadUrl($input: GenerateBatchUpsertUploadUrlInput!) {
  generateBatchUpsertUploadUrl(input: $input) {
    uploadUrl
    batchUpsertReference
    expiresAt
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetTypeId": "equipment",
    "fileFormat": "CSV",
    "fileName": "equipment_batch_update.csv",
    "fileSizeBytes": 2048000,
    "group": "your-tenant-id",
    "expirationMinutes": 60
  }
}
```

### Upload File and Trigger Batch Upsert

```javascript theme={null}
// 1. Get the upload URL
const { data } = await client.query({
  query: GENERATE_UPLOAD_URL,
  variables: { input: uploadParams }
})

// 2. Upload file to S3
await fetch(data.generateBatchUpsertUploadUrl.uploadUrl, {
  method: 'PUT',
  headers: { 'Content-Type': 'text/csv' },
  body: csvFileContent
})

// 3. Trigger batch processing
await client.mutate({
  mutation: gql`
    mutation BatchUpsert($ref: String!) {
      manageCamAssets(dryRun: false) {
        batchUpsert(batchUpsertReference: $ref) {
          success
          processedCount
          errors {
            row
            message
          }
        }
      }
    }
  `,
  variables: {
    ref: data.generateBatchUpsertUploadUrl.batchUpsertReference
  }
})
```

## Asset Hierarchy Operations

### Query Asset Hierarchy

```graphql theme={null}
query GetAssetHierarchy($rootId: ID!) {
  camAssets {
    byId(id: $rootId) {
      id
      name
      assetTypeId
      children {
        id
        name
        assetTypeId
        children {
          id
          name
          assetTypeId
        }
      }
    }
  }
}
```

### Create Child Asset

```graphql theme={null}
mutation CreateChildAsset($input: CreateAssetInput!) {
  createAsset(input: $input) {
    asset {
      id
      name
      parent {
        id
        name
      }
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

**Variables:**

```json theme={null}
{
  "input": {
    "assetId": "compressor-001",
    "name": "Compressor Unit 1",
    "assetTypeId": "equipment",
    "active": true,
    "group": "your-tenant-id",
    "parent": {
      "assetId": "site-abc",
      "assetTypeId": "site"
    },
    "geometry": {
      "type": "Point",
      "coordinates": [-114.0720, 51.0448]
    }
  }
}
```

## Geospatial Queries

CAM supports full GeoJSON geometry types for asset locations:

### Query Assets by Bounding Box

```graphql theme={null}
query GetAssetsInBounds(
  $assetTypeId: ID!
  $minLon: Float!
  $minLat: Float!
  $maxLon: Float!
  $maxLat: Float!
) {
  camAssets(assetTypeId: $assetTypeId) {
    all(
      first: 100
      filter: {
        geometry: {
          bbox: [$minLon, $minLat, $maxLon, $maxLat]
        }
      }
    ) {
      edges {
        node {
          id
          name
          geometry {
            type
            coordinates
            bbox
          }
        }
      }
    }
  }
}
```

### Supported Geometry Types

CAM supports all GeoJSON geometry types:

* **Point**: Single location `[-114.0719, 51.0447]`
* **LineString**: Linear features (pipelines, roads)
* **Polygon**: Areas and boundaries
* **MultiPoint**, **MultiLineString**, **MultiPolygon**: Collections
* **GeometryCollection**: Mixed geometry types

## Asset References

Query lightweight asset references for dropdowns and lookups:

```graphql theme={null}
query GetAssetReferences {
  camAssetReferences {
    all(first: 100) {
      edges {
        node {
          id
          name
          assetTypeId
        }
      }
    }
  }
}
```

## CAM Engine and Datasets

Query available datasets and engine configuration:

```graphql theme={null}
query GetCamEngine {
  camEngine {
    datasets {
      id
      name
      assetTypeId
      sourceType
    }
  }
}
```

### Get Datasets for Tenant

```graphql theme={null}
query GetCamDatasets($group: ID!) {
  camAssetsDatasets(group: $group) {
    dataset
    displayName
    assetTypeId
  }
}
```

## Asset Configuration

Manage tenant-specific asset configurations:

```graphql theme={null}
query GetAssetConfigs($group: ID!) {
  assetConfigs(group: $group) {
    all {
      id
      value
    }
  }
}
```

### Set Configuration

```graphql theme={null}
mutation SetAssetConfig($group: ID!, $id: ID!, $value: JSONObject!) {
  setAssetConfig(group: $group, id: $id, value: $value) {
    config {
      id
      value
    }
    correlationId
    errors {
      message
      type
    }
  }
}
```

## Best Practices

### 1. Use Asset Type Filtering

Always filter by `assetTypeId` when querying assets to improve performance:

```graphql theme={null}
# Good - filtered by type
camAssets(assetTypeId: "equipment") { all(first: 20) { ... } }

# Less efficient - all asset types
camAssets { all(first: 20) { ... } }
```

### 2. Batch Operations for Scale

For bulk operations (>100 assets), use batch upsert instead of individual mutations:

```graphql theme={null}
# Good for >100 assets
generateBatchUpsertUploadUrl → upload CSV → manageCamAssets.batchUpsert

# Use individual mutations for <100 assets
createAsset, updateAsset
```

### 3. Request Only Needed Fields

Minimize query size by selecting only required fields:

```graphql theme={null}
# Good - specific fields
camAssets { byId(id: "x") { id name } }

# Avoid - requesting all properties
camAssets { byId(id: "x") { id name properties ... } }
```

### 4. Use Asset References for Lookups

For dropdowns and selection lists, use `camAssetReferences` instead of full `camAssets`:

```graphql theme={null}
# Good - lightweight references
camAssetReferences { all { edges { node { id name } } } }

# Avoid - full asset queries for simple lookups
camAssets { all { edges { node { id name ... } } } }
```

### 5. Leverage Asset Hierarchy

Use parent-child relationships instead of flat structures:

```graphql theme={null}
# Good - hierarchical query
camAssets {
  byId(id: "site-001") {
    children { # Equipment at site
      children { # Components on equipment
        id
        name
      }
    }
  }
}
```

## Error Handling

CAM mutations return errors in the standard format:

```json theme={null}
{
  "data": {
    "createAsset": {
      "asset": null,
      "correlationId": "abc-123",
      "errors": [
        {
          "message": "Asset with ID 'site-abc' already exists",
          "type": "DUPLICATE_ID"
        }
      ]
    }
  }
}
```

Common error types:

* `DUPLICATE_ID`: Asset ID already exists
* `INVALID_PARENT`: Parent asset not found or invalid type
* `INVALID_PROPERTY`: Property validation failed
* `INVALID_GEOMETRY`: GeoJSON geometry is malformed
* `MISSING_REQUIRED`: Required field not provided

## Related Resources

* **[Subgraph Reference](../subgraphs/cam-assets)** - Complete schema documentation
* **[Common Patterns](../common-patterns)** - Pagination and filtering
* **[Field Service Guide](./field-service)** - Mobile workflows using assets
* **[Core Platform Guide](./core-platform)** - Maps and geospatial services
