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
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
query GetAssetTypes {
assetTypes {
all(first: 20) {
edges {
node {
assetTypeId
name
description
properties {
id
name
dataType
required
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Get Specific Asset Type
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:
{
"assetTypeId": "site"
}
Query Assets by Type
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:
{
"assetTypeId": "equipment"
}
Query Asset by ID
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:
Filter and Search Assets
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:
{
"assetTypeId": "site",
"searchText": "compressor"
}
Creating and Updating Assets
Create an Asset
mutation CreateAsset($input: CreateAssetInput!) {
createAsset(input: $input) {
asset {
id
name
assetTypeId
properties
}
correlationId
errors {
message
type
}
}
}
Variables:
{
"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
mutation UpdateAssetProperty($input: UpdateAssetPropertyInput!) {
updateAssetProperty(input: $input) {
asset {
id
properties
}
correlationId
errors {
message
type
}
}
}
Variables:
{
"input": {
"assetId": "site-abc",
"group": "your-tenant-id",
"propertyValues": [
{
"propertyId": "capacity",
"value": "6000"
}
]
}
}
Update Asset Geometry
mutation UpdateAssetGeometry($input: UpdateAssetGeometryInput!) {
updateAssetGeometry(input: $input) {
asset {
id
geometry {
type
coordinates
}
}
correlationId
errors {
message
type
}
}
}
Variables:
{
"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
mutation CreateAssetStatus($input: CreateAssetStatusInput!) {
createAssetStatus(input: $input) {
assetStatus {
assetId
statusId
label
setAt
setBy
}
correlationId
errors {
message
type
}
}
}
Variables:
{
"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
query GenerateUploadUrl($input: GenerateBatchUpsertUploadUrlInput!) {
generateBatchUpsertUploadUrl(input: $input) {
uploadUrl
batchUpsertReference
expiresAt
}
}
Variables:
{
"input": {
"assetTypeId": "equipment",
"fileFormat": "CSV",
"fileName": "equipment_batch_update.csv",
"fileSizeBytes": 2048000,
"group": "your-tenant-id",
"expirationMinutes": 60
}
}
Upload File and Trigger Batch Upsert
// 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
query GetAssetHierarchy($rootId: ID!) {
camAssets {
byId(id: $rootId) {
id
name
assetTypeId
children {
id
name
assetTypeId
children {
id
name
assetTypeId
}
}
}
}
}
Create Child Asset
mutation CreateChildAsset($input: CreateAssetInput!) {
createAsset(input: $input) {
asset {
id
name
parent {
id
name
}
}
correlationId
errors {
message
type
}
}
}
Variables:
{
"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
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:
query GetAssetReferences {
camAssetReferences {
all(first: 100) {
edges {
node {
id
name
assetTypeId
}
}
}
}
}
CAM Engine and Datasets
Query available datasets and engine configuration:
query GetCamEngine {
camEngine {
datasets {
id
name
assetTypeId
sourceType
}
}
}
Get Datasets for Tenant
query GetCamDatasets($group: ID!) {
camAssetsDatasets(group: $group) {
dataset
displayName
assetTypeId
}
}
Asset Configuration
Manage tenant-specific asset configurations:
query GetAssetConfigs($group: ID!) {
assetConfigs(group: $group) {
all {
id
value
}
}
}
Set Configuration
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:
# 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:
# 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:
# 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:
# 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:
# 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:
{
"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