Browse docs

Asset Sync

Synchronize asset data from your asset management or telematics systems to LambdaAssetCheck. Create, update, and manage asset records, assign meters, and sync meter readings in a single batch operation.

v1.0

Key Features

Batch Processing

Process 100-500 assets per request. Each asset is processed independently for reliability.

Idempotent Operations

Safe to retry failed requests. Same externalAssetId with same data produces the same result.

Meter Management

Assign meters to assets, sync current readings, and upload historical meter reading data.

Telematics Support

Track meters expected from sync, monitor last seen timestamps, and detect missing syncs.

Request Body Schema

Minimal Request (Required Fields Only)

When creating a new asset, externalAssetId, name, assetTypeName, and siteCode are all required. The asset type and site must already exist in your organization (create them in-app first). On updates to an existing asset (matched by code or externalAssetId), assetTypeName and siteCode are optional.

Example: Simple Asset
{
  "assets": [
    {
      "externalAssetId": "ASSET-001",
      "name": "Water Pump",
      "assetTypeName": "Pump",
      "siteCode": "SITE-42"
    }
  ]
}

Complete Example Requests

Example 1: Regular Sync (Without Historical Readings)

Use this pattern for daily/hourly syncs. Uses currentReading in meters[] array.

{
  "syncBatchId": "batch-2024-11-18-001",
  "assets": [
    {
      "externalAssetId": "ASSET-100245",
      "code": "PUMP-001",
      "name": "Main Water Pump",
      "assetTypeName": "Pump",
      "siteCode": "SITE-42",
      "serial": "SN-12345",
      "status": "ACTIVE",
      "description": "Primary water pump for building A",
      "customFields": {
        "manufacturer": "ABC Pumps",
        "model": "XP-5000",
        "installationDate": "2020-01-15"
      },
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "isActive": true,
          "isApplicable": true,
          "equipmentStatus": "EQUIPPED",
          "expectedFromSync": true,
          "currentReading": {
            "value": 25000,
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL",
            "note": "Latest reading from telematics"
          }
        },
        {
          "meterCode": "HOURS",
          "isActive": true,
          "isApplicable": true,
          "equipmentStatus": "EQUIPPED",
          "expectedFromSync": true,
          "currentReading": {
            "value": 5000,
            "unit": "HOURS",
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL"
          }
        }
      ]
    },
    {
      "externalAssetId": "ASSET-100246",
      "code": "VEHICLE-001",
      "name": "Delivery Truck #1",
      "assetTypeName": "Vehicle",
      "siteCode": "WAREHOUSE-A",
      "serial": "VIN-987654321",
      "status": "ACTIVE",
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "isActive": true,
          "expectedFromSync": true,
          "equipmentStatus": "EQUIPPED",
          "currentReading": {
            "value": 125000,
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL"
          }
        }
      ]
    }
  ]
}
Response: Regular Sync
{
  "syncBatchId": "batch-2024-11-18-001",
  "results": [
    {
      "externalAssetId": "ASSET-100245",
      "status": "UPDATED",
      "assetId": "asset_9b3c8d7e6f5a4b3c2d1e0f",
      "warnings": []
    },
    {
      "externalAssetId": "ASSET-100246",
      "status": "UPDATED",
      "assetId": "asset_8a2b7c6d5e4f3a2b1c0d9e",
      "warnings": []
    }
  ],
  "errors": []
}
Example 2: Initial Setup with Historical Readings

Use this pattern for first-time sync or backfill. Uses both currentReading and meterReadings array.

{
  "syncBatchId": "batch-initial-setup-001",
  "assets": [
    {
      "externalAssetId": "ASSET-100245",
      "code": "PUMP-001",
      "name": "Main Water Pump",
      "assetTypeName": "Pump",
      "siteCode": "SITE-42",
      "serial": "SN-12345",
      "status": "ACTIVE",
      "description": "Primary water pump for building A",
      "customFields": {
        "manufacturer": "ABC Pumps",
        "model": "XP-5000",
        "installationDate": "2020-01-15"
      },
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "isActive": true,
          "isApplicable": true,
          "equipmentStatus": "EQUIPPED",
          "initialReading": 15000,
          "installedAt": "2020-01-15T00:00:00Z",
          "expectedFromSync": true,
          "validationThresholds": {
            "maxDeltaPerDay": 1000
          },
          "currentReading": {
            "value": 25000,
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL",
            "note": "Latest reading from telematics"
          }
        },
        {
          "meterCode": "HOURS",
          "isActive": true,
          "isApplicable": true,
          "equipmentStatus": "EQUIPPED",
          "initialReading": 0,
          "expectedFromSync": true,
          "currentReading": {
            "value": 5000,
            "unit": "HOURS",
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL"
          }
        }
      ],
      "meterReadings": [
        {
          "meterCode": "ODOMETER_KM",
          "value": 24000,
          "recordedAt": "2024-11-17T10:00:00Z",
          "source": "SYNC",
          "status": "NORMAL"
        },
        {
          "meterCode": "ODOMETER_KM",
          "value": 24500,
          "recordedAt": "2024-11-17T18:00:00Z",
          "source": "SYNC",
          "status": "NORMAL"
        },
        {
          "meterCode": "ODOMETER_KM",
          "value": 24800,
          "unit": "KM",
          "recordedAt": "2024-11-18T08:00:00Z",
          "source": "SYNC",
          "status": "NORMAL"
        }
      ],
      "metadata": {
        "externalSystem": "FleetManager",
        "lastSync": "2024-11-18T10:30:00Z"
      }
    },
    {
      "externalAssetId": "ASSET-100246",
      "code": "VEHICLE-001",
      "name": "Delivery Truck #1",
      "assetTypeName": "Vehicle",
      "siteCode": "WAREHOUSE-A",
      "serial": "VIN-987654321",
      "status": "ACTIVE",
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "isActive": true,
          "expectedFromSync": true,
          "equipmentStatus": "EQUIPPED",
          "currentReading": {
            "value": 125000,
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC",
            "status": "NORMAL"
          }
        }
      ],
      "meterReadings": [
        {
          "meterCode": "ODOMETER_KM",
          "value": 123000,
          "unit": "KM",
          "recordedAt": "2024-11-17T08:00:00Z",
          "source": "SYNC",
          "status": "NORMAL"
        },
        {
          "meterCode": "ODOMETER_KM",
          "value": 124000,
          "unit": "KM",
          "recordedAt": "2024-11-17T18:00:00Z",
          "source": "SYNC",
          "status": "NORMAL"
        }
      ]
    }
  ]
}
Response: Initial Setup with Historical Readings
{
  "syncBatchId": "batch-initial-setup-001",
  "results": [
    {
      "externalAssetId": "ASSET-100245",
      "status": "CREATED",
      "assetId": "asset_9b3c8d7e6f5a4b3c2d1e0f",
      "warnings": []
    },
    {
      "externalAssetId": "ASSET-100246",
      "status": "CREATED",
      "assetId": "asset_8a2b7c6d5e4f3a2b1c0d9e",
      "warnings": []
    }
  ],
  "errors": []
}

Asset Fields

FieldTypeRequiredDescription
externalAssetIdstringRequiredUnique identifier from your external system. Used as the primary key for upsert operations. Must be unique within your organization.
namestringRequiredAsset name.
codestringOptionalAsset code. If not provided, externalAssetId is used as the code. Must be unique within your organization.
assetTypeNamestringRequired (create)Asset type name (matched by name within your organization). Must exist in-app before syncing. Required when creating a new asset; optional on update. If provided but the asset type doesn't exist, the sync fails with an error.
siteCodestringRequired (create)Site code (matched by code within your organization). Must exist in-app before syncing. Required when creating a new asset; optional on update. If provided but the site doesn't exist, the sync fails with an error on create (warning only on update).
serialstringOptionalSerial number of the asset.
statusenumOptionalAsset status: ACTIVE, INACTIVE, or RETIRED. Defaults to ACTIVE. Setting to INACTIVE or RETIRED deactivates all asset meters.
descriptionstringOptionalAsset description.
customFieldsobjectOptionalCustom fields as a JSON object. Can store any additional information about the asset (e.g., manufacturer, model, installation date).
metersarrayOptionalArray of meter assignment objects. See Meters and Readings section below.
meterReadingsarrayOptionalArray of historical meter reading objects. See Meters and Readings section below.
metadataobjectOptionalAdditional metadata as a JSON object. Can store any additional information (e.g., external system name, last sync timestamp).

Meters and Readings

Meter Assignment

The meters array assigns meters to assets and optionally includes the current reading:

Example: Asset with Meters
{
  "syncBatchId": "batch-003",
  "assets": [
    {
      "externalAssetId": "ASSET-003",
      "code": "VEHICLE-001",
      "name": "Delivery Truck #1",
      "assetTypeName": "Vehicle",
      "siteCode": "WAREHOUSE-A",
      "status": "ACTIVE",
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "isActive": true,
          "expectedFromSync": true,
          "equipmentStatus": "EQUIPPED",
          "currentReading": {
            "value": 25000,
            "unit": "KM",
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC"
          }
        }
      ]
    }
  ]
}
FieldTypeRequiredDescription
meterCodestringRequiredMeter code (matched by exact code - case-sensitive). Meter must exist in your organization. If not found, the meter assignment is rejected, a warning is added, and the attempt is logged for admin review in Admin → Meters → Missing Meters.
isActivebooleanOptionalWhether the meter is active for this asset. Defaults to true.
isApplicablebooleanOptionalWhether the meter applies to this asset. Defaults to true.
equipmentStatusenumOptionalEquipment status: UNKNOWN, EQUIPPED, or NOT_EQUIPPED. Defaults to UNKNOWN.
initialReadingnumberOptionalInitial reading value when the meter was installed.
installedAtstringOptionalInstallation date in ISO 8601 format (e.g., 2020-01-15T00:00:00Z).
expectedFromSyncbooleanOptionalWhether readings are expected from sync (for telematics tracking). Defaults to false. When true, the system tracks lastSeenInSyncAt and consecutiveMissingSyncs.
validationThresholdsobjectOptionalValidation thresholds as a JSON object (e.g., { "maxDeltaPerDay": 1000 }).
currentReadingobjectOptionalCurrent reading object. See Current Reading section below.

Current Reading

The currentReading object within a meter assignment provides the latest reading value:

FieldTypeRequiredDescription
valuenumberRequiredReading value (integer or decimal).
recordedAtstringOptionalRecording date in ISO 8601 format. Defaults to current time if not provided.
sourceenumOptionalReading source: SYNC or INSPECTION. Defaults to SYNC.
statusenumOptionalReading status: NORMAL or NOT_EQUIPPED. Defaults to NORMAL.
notestringOptionalOptional note about the reading.

Unit Validation (Strict)

ScenarioMeter UnitReading UnitResult
Correct unitKMKM✅ Accepted
Case mismatchKMkm❌ Rejected
Wrong unitKMMILES❌ Rejected
Missing unitKMnot provided❌ Rejected
Error Messages
  • Missing unit: "Unit is required for meter 'ODOMETER_KM'. Please specify the unit in your sync payload."
  • Unit mismatch: "Unit mismatch for meter 'ODOMETER_KM': expected 'KM', received 'MILES'. Reading rejected to prevent data corruption."
Common Units
Distance: KM, MILES, M, FT
Time: HOURS, MINUTES, SECONDS
Energy: KWH, MWH, KW
Pressure: PSI, BAR, KPA
Temperature: C, F
Volume: LITRE, GALLON, M3

Historical Meter Readings

The meterReadings array allows you to upload historical meter reading data:

{
  "meterReadings": [
    {
      "meterCode": "ODOMETER_KM",
      "value": 24000,
      "unit": "KM",
      "recordedAt": "2024-11-17T10:00:00Z",
      "source": "SYNC",
      "status": "NORMAL"
    },
    {
      "meterCode": "ODOMETER_KM",
      "value": 24500,
      "unit": "KM",
      "recordedAt": "2024-11-17T18:00:00Z",
      "source": "SYNC",
      "status": "NORMAL"
    }
  ]
}
FieldTypeRequiredDescription
meterCodestringRequiredMeter code (matched by exact code - case-sensitive). Meter must exist in your organization. If not found, the reading is rejected, a warning is added, and the attempt is logged for admin review in Admin → Meters → Missing Meters.
valuenumberRequiredReading value (integer or decimal).
unitstringRequiredUnit must match meter's unit exactly (case-sensitive). Prevents data corruption. Example: "KM", "HOURS", "PSI", "C". If unit is missing or doesn't match, reading is rejected.
recordedAtstringOptionalRecording date in ISO 8601 format. Defaults to current time if not provided.
sourceenumOptionalReading source: SYNC or INSPECTION. Defaults to SYNC.
statusenumOptionalReading status: NORMAL or NOT_EQUIPPED. Defaults to NORMAL.
notestringOptionalOptional note about the reading.

When to Use currentReading vs meterReadings

Use currentReading
For Regular Syncs (Daily/Hourly)
  • ✅ Updates telematics tracking automatically
  • ✅ Sets lastSeenInSyncAt timestamp
  • ✅ Resets consecutiveMissingSyncs to 0
  • ✅ More efficient (one reading per meter)
  • ✅ Faster processing
  • ✅ Enables health monitoring
Example:
{
  "assets": [
    {
      "externalAssetId": "VEHICLE-001",
      "meters": [
        {
          "meterCode": "ODOMETER_KM",
          "expectedFromSync": true,
          "currentReading": {
            "value": 25000,
            "unit": "KM",
            "recordedAt": "2024-11-18T10:30:00Z",
            "source": "SYNC"
          }
        }
      ]
    }
  ]
}
Use meterReadings
For Historical Data / Initial Setup
  • ✅ Duplicate protection (same value within 1 second)
  • ✅ Bulk upload support (hundreds of readings)
  • ✅ Efficient for historical backfill
  • ✅ Creates AssetMeter automatically if needed
  • ❌ Does NOT update telematics tracking
  • ❌ No lastSeenInSyncAt update
Example:
{
  "assets": [
    {
      "externalAssetId": "VEHICLE-001",
      "meterReadings": [
        {
          "meterCode": "ODOMETER_KM",
          "value": 24000,
          "unit": "KM",
          "recordedAt": "2024-11-17T10:00:00Z",
          "source": "SYNC"
        },
        {
          "meterCode": "ODOMETER_KM",
          "value": 24500,
          "unit": "KM",
          "recordedAt": "2024-11-17T18:00:00Z",
          "source": "SYNC"
        }
      ]
    }
  ]
}

Decision Guide

ScenarioWhat to SendWhy
Regular Sync
(Daily/Hourly)
currentReading in meters[]Updates telematics tracking, efficient, enables health monitoring
Initial Setup
(First Time Sync)
currentReading + meterReadingsLatest reading updates tracking, historical data fills in the past
Catch-Up After Downtime
(Missed Syncs)
currentReading + meterReadingsLatest reading updates tracking, missed readings fill the gap
Historical Backfill
(Bulk Import)
meterReadings onlyDuplicate protection, efficient bulk upload

Matching Logic

The system matches entities using the following logic:

EntityMatch ByExample
Assetcode or externalAssetId{ "code": "PUMP-001" }
Asset Typename{ "assetTypeName": "Pump" }
Sitecode{ "siteCode": "SITE-42" }
Metercode (exact match - case-sensitive){ "meterCode": "ODOMETER_KM" }
Note: Must match exactly. "ODOMETER_KM" ≠ "odometer_km" ≠ "ODOMETER_KM_H"

Response Schema

Success Response

Example: All assets processed successfully
{
  "syncBatchId": "batch-2024-11-18-001",
  "results": [
    {
      "externalAssetId": "ASSET-100245",
      "status": "UPDATED",
      "assetId": "asset_9b3c8d7e6f5a4b3c2d1e0f",
      "warnings": []
    },
    {
      "externalAssetId": "ASSET-100246",
      "status": "CREATED",
      "assetId": "asset_8a2b7c6d5e4f3a2b1c0d9e",
      "warnings": []
    },
    {
      "externalAssetId": "ASSET-100247",
      "status": "CREATED",
      "assetId": "asset_7z1y6x5w4v3u2t1s0r9q",
      "warnings": []
    }
  ],
  "errors": []
}

Partial Success Response

Example: Some assets succeeded, others failed
{
  "syncBatchId": "batch-2024-11-18-002",
  "results": [
    {
      "externalAssetId": "ASSET-100245",
      "status": "CREATED",
      "assetId": "asset_9b3c8d7e6f5a4b3c2d1e0f",
      "warnings": [
        "Meter with exact code 'HOURS' not found. Please create the meter in Admin → Meters → Missing Meters before syncing."
      ]
    }
  ],
  "errors": [
    {
      "externalAssetId": "ASSET-777",
      "message": "name is required"
    },
    {
      "externalAssetId": "ASSET-888",
      "message": "Asset type 'NonExistentType' not found. Please create the asset type in-app before syncing assets."
    },
    {
      "externalAssetId": "ASSET-999",
      "message": "siteCode is required when creating an asset. Create the site in-app first, then include its code in the sync payload."
    }
  ]
}
FieldTypeDescription
syncBatchIdstringEcho of the syncBatchId from your request, if provided.
resultsarrayArray of asset processing results. Each result contains:
  • externalAssetId - The asset ID from your request
  • status - CREATED, UPDATED, DISABLED, or FAILED
  • assetId - Internal asset ID (present if successful)
  • warnings - Array of warning messages (e.g., missing asset types, sites, or meters)
errorsarrayArray of error objects for assets that failed to process. Each error contains:
  • externalAssetId - The asset ID from your request
  • message - Error message describing what went wrong

Status Values

StatusDescription
CREATEDAsset was newly created
UPDATEDExisting asset was updated
DISABLEDAsset was retired or inactivated
FAILEDAsset processing failed (check errors array for details)

Best Practices

Batch Size

Process assets in batches of 100-500 for optimal performance. Smaller batches (50-100) are recommended for initial syncs or when dealing with complex asset data including many meters and readings.

Idempotency

The endpoint is idempotent. You can safely retry the same request multiple times. The externalAssetId is used as the unique key, so sending the same asset data multiple times will result in updates rather than duplicates.

Error Handling

Always check both the results and errors arrays in the response. Even if some assets fail, others may have succeeded. Implement retry logic for transient errors (e.g., rate limiting, temporary database issues).

Date Formats

Always use ISO 8601 format for dates (e.g., 2024-11-18T10:30:00Z). See Date/Time Format documentation for details.

Prerequisites

Before syncing assets, ensure the following exist in your organization:

  • Asset Types: Create asset types in-app before syncing. assetTypeName is required when creating a new asset.
  • Sites: Create sites in-app before syncing. siteCode is required when creating a new asset.
  • Meters: Create meters in-app before syncing assets with meterCode. Meters must match exactly(case-sensitive). If a meter doesn't exist, the sync willreject the meter assignment/reading and log it for admin review.

Missing or unknown asset types and sites cause the sync to fail with an error when creating a new asset. Fix the payload or create the referenced records in-app, then retry.

Missing Meters Workflow

When a meter code doesn't exist (exact match required), the system follows this workflow:

1
Reject

Meter assignment or reading is rejected. The meter code must match exactly (case-sensitive). No auto-creation occurs.

2
Log

Attempt is logged in MissingMeterAttempt table with full context (asset info, readings, sync type, etc.).

3
Review

Admin reviews attempts in Admin → Meters → Missing Meters, creates the meter, and retries the sync.

Telematics Integration

For telematics integrations:

  • Set expectedFromSync: true for meters that should receive readings from sync
  • Use currentReading in meter assignments for the latest reading (most efficient)
  • Use meterReadings array for uploading historical reading data
  • Monitor lastSeenInSyncAt and consecutiveMissingSyncs for health monitoring
  • Sync regularly (daily or hourly) to keep data current

Asset Status Management

When managing asset status:

  • Set status: "RETIRED" or "INACTIVE" to deactivate assets and all their meters
  • Historical data (readings, inspections) is preserved when assets are retired
  • Retired assets are no longer available for new inspections

Common Error Scenarios

Validation Errors

Error MessageCauseSolution
externalAssetId is requiredMissing or empty externalAssetIdEnsure every asset has a unique externalAssetId
name is requiredMissing or empty nameProvide a name field for every asset
assetTypeName is required when creating an asset.New asset without assetTypeNameInclude assetTypeName matching an existing asset type in your organization
siteCode is required when creating an asset.New asset without siteCodeInclude siteCode matching an existing site in your organization
assets array is requiredMissing assets array in requestInclude an assets array in your request
assets array cannot be emptyEmpty assets arrayInclude at least one asset in the assets array

Resource Not Found Errors and Warnings

The following scenarios result in errors or warnings:

The following scenarios result in warnings (not errors) - the asset will still be processed, but the missing resources will be skipped:

Warning MessageCauseSolution
Asset type 'X' not found. Please create the asset type in-app before syncing assets.Asset type doesn't exist in your organizationCreate the asset type in-app first, then sync with the correct assetTypeName.
Site with code 'X' not found. Please create the site in-app before syncing assets.Site doesn't exist in your organization (on create)Create the site in-app first, then sync with the correct siteCode
Site with code 'X' not found. Please create the site in-app before syncing assets.Site doesn't exist when updating an existing assetWarning only — asset is updated but site is unchanged. Create the site and retry with siteCode to assign it.
Meter with exact code 'X' not found. Please create the meter in Admin → Meters → Missing Meters before syncing.Meter doesn't exist in your organization (exact code match required - case-sensitive)Create the meter in-app first, then sync. The attempt is logged in Admin → Meters → Missing Meters for review. Meter codes must match exactly (case-sensitive).
Meter with exact code 'X' not found. Please create the meter in Admin → Meters → Missing Meters before syncing.Meter doesn't exist when syncing meter readings (exact code match required - case-sensitive)Create the meter in-app first, then sync readings. The attempt is logged in Admin → Meters → Missing Meters for review. Meter codes must match exactly (case-sensitive).

Always check the warnings array in the response to identify missing resources.

Authentication Errors

Error MessageCauseSolution
Invalid tokenInvalid or expired API keyCheck your API token and regenerate if necessary
Missing Authorization headerNo Authorization header in requestInclude Authorization header with Bearer token or Basic auth

Rate Limiting

Top