Skip to Content

Asset Track

Send telemetry data for an asset to ModularIoT.

Endpoint

POST https://iot.streamhub.cl/v1/asset/track

Authentication

Requires a valid Bearer token with asset:track:write scope.

Authorization: Bearer <access_token>

Request

Headers

HeaderRequiredDescription
AuthorizationYesBearer <access_token>
Content-TypeYesapplication/json
AcceptRecommendedapplication/json for JSON responses
X-Request-IdYesUnique identifier for request tracing
X-Request-TimestampYesRequest timestamp (Unix epoch seconds)

Body (AssetTrackingData)

{ "asset_id": "A123456789", "timestamp": "2024-10-22T14:23:45Z", "type": "Truck", "owner": "Company ABC", "year": 2020, "gps": { "latitude": -33.4489, "longitude": -70.6693, "altitude": 520, "speed": 65.5, "heading": 180 }, "metrics": { "schema": "miot.metrics@1.0", "items": [ { "k": "engine.rpm", "v": 2500, "u": "rpm", "src": "obd2", "q": "ok" }, { "k": "vehicle.speed", "v": 72, "u": "km/h", "src": "obd2", "q": "ok" }, { "k": "fuel.level", "v": 65, "u": "%", "src": "obd2", "q": "ok" } ], "seq": 12345 } }

Field Reference

FieldTypeRequiredDescription
asset_idstringYesUnique identifier for the asset
timestampstringYesISO 8601 timestamp with timezone
typestringNoAsset type (e.g., “Truck”, “Container”)
ownerstringNoAsset owner name
yearintegerNoYear of manufacture
gpsobjectNoGPS location data
telecomobjectNoConnectivity information
sensorsobjectNoSensor readings
driver_infoobjectNoDriver information
co_driver_infoobjectNoCo-driver information
peripheralsobjectNoConnected peripherals
metricsobjectNoCanonical metrics (miot.metrics@1.0)
eventsarrayNoDiscrete events

GPS Object

{ "latitude": -33.4489, "longitude": -70.6693, "altitude": 520, "speed": 65.5, "heading": 180, "accuracy": 5 }
FieldTypeUnitDescription
latitudenumberdegreesWGS84 latitude (-90 to 90)
longitudenumberdegreesWGS84 longitude (-180 to 180)
altitudenumbermetersAltitude above sea level
speednumberkm/hGround speed
headingnumberdegreesDirection (0-360)
accuracynumbermetersGPS accuracy

Metrics Object (miot.metrics@1.0)

For sending canonical vehicle/IoT metrics using the standardized envelope:

{ "schema": "miot.metrics@1.0", "items": [ { "k": "engine.rpm", "v": 2150, "u": "rpm", "src": "obd2", "q": "ok" }, { "k": "engine.coolant_temp", "v": 91, "u": "C", "src": "obd2", "q": "ok" }, { "k": "battery.voltage", "v": 13.9, "u": "V", "src": "obd2", "q": "ok" }, { "k": "vehicle.speed", "v": 65, "u": "km/h", "src": "obd2", "q": "ok" } ], "seq": 12345, "device_ts": "2024-10-22T14:23:45Z" }

MetricsEnvelope Fields

FieldTypeRequiredDescription
schemastringYesMust be "miot.metrics@1.0"
itemsarrayYesArray of MetricItem objects (min 1, max 200)
seqintegerNoDevice sequence number for ordering/deduplication
device_tsstringNoDevice clock timestamp (RFC3339)
capabilitiesobjectNoOpaque metadata about supported metrics

MetricItem Fields

FieldTypeRequiredDescription
kstringYesCanonical metric key (e.g., engine.rpm)
vanyYesValue (number, boolean, string, object, or array)
ustringNoCanonical unit (e.g., rpm, km/h, C, V, %)
tsstringNoTimestamp override for this item (RFC3339)
srcstringNoSource: obd2, j1939, can, oem, derived, unknown
qstringNoQuality: ok, estimated, stale, invalid, missing
metaobjectNoOpaque metadata for traceability (PID, SPN, CAN ID)

Key pattern: Metric keys must match ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*){1,5}$ (2-6 dot-separated lowercase segments)

→ See Reference > Metrics Registry for all available canonical metric keys.

Sensors Object

For GPS devices that send sensor data directly (without OBD-II or CAN BUS connection):

{ "acceleration": { "x_axis": 1.5, "y_axis": -0.5, "z_axis": 2.0, "g_force": 1.2 }, "gyroscope": { "rotation_rate_x": 5.5, "rotation_rate_y": -3.2, "rotation_rate_z": 1.8 }, "odometer": 125432.5, "engine_status": 1 }
FieldTypeDescription
accelerationobjectLinear acceleration from the device’s accelerometer (see below)
gyroscopeobjectAngular rotation rates from the device’s gyroscope (see below)
odometernumberTotal distance traveled in kilometers
engine_statusintegerEngine state: 1 = on, 0 = off

Use this object when the GPS device provides inertial, odometer or engine status readings directly, without relying on the vehicle’s diagnostic bus. All sub-fields are optional — send only what the device reports.

Acceleration

Linear acceleration on each axis of the device’s accelerometer, plus the total magnitude. Used to detect harsh braking, hard acceleration, sharp cornering, impacts and crash events.

FieldTypeUnitRangeDescription
x_axisnumberg-3.0 to 3.0Acceleration along the X axis (typically lateral)
y_axisnumberg-3.0 to 3.0Acceleration along the Y axis (typically longitudinal)
z_axisnumberg-3.0 to 3.0Acceleration along the Z axis (typically vertical)
g_forcenumberg0.0 to 5.0Total acceleration magnitude, including gravity

Values are reported in units of g (standard gravity, 1 g ≈ 9.80665 m/s²). Axis orientation depends on how the device is mounted — refer to the device datasheet for the sensor frame.

Gyroscope

Angular rotation rate around each axis of the device’s gyroscope. Used to detect rollover, aggressive maneuvers and orientation changes.

FieldTypeUnitRangeDescription
rotation_rate_xnumberrad/s-10.0 to 10.0Rotation rate around the X axis (roll)
rotation_rate_ynumberrad/s-10.0 to 10.0Rotation rate around the Y axis (pitch)
rotation_rate_znumberrad/s-10.0 to 10.0Rotation rate around the Z axis (yaw)

Values are reported in radians per second. Multiply by 180/π (≈ 57.2958) to convert to degrees per second if your downstream pipeline expects °/s.

Events Object

For GPS devices or providers that pre-compute discrete asset events (harsh braking, hard acceleration, sharp cornering, speeding, impacts). Use this object when the provider already delivers the typed event — with severity and threshold — rather than only the raw sensor reading.

{ "events": [ { "event_type": "hard_brake", "timestamp": "2026-04-23T14:32:10Z", "severity": "high", "threshold_exceeded": true, "additional_info": { "g_force": 0.82, "speed_at_event": 74.5, "cargo_weight": 5000 } } ] }
FieldTypeRequiredDescription
event_typestringYesEvent type. See catalog below.
timestampstringYesISO 8601 timestamp of the event (may differ from the envelope timestamp).
severitystringNoSeverity reported by the provider: low, medium, high.
threshold_exceededbooleanNotrue if the event exceeded the provider’s configured threshold.
additional_infoobjectNoEvent metadata (see below).

event_type catalog

ValueDescription
hard_brakeHarsh braking detected by the provider from longitudinal acceleration.
hard_accelHard acceleration detected by the provider from longitudinal acceleration.
hard_turnSharp cornering / aggressive lateral maneuver detected by the provider from the gyroscope or lateral acceleration.
speedingOver-speed relative to the configured limit.
impactImpact / collision detected by the provider from a g-force spike.

The event_type field is a free-form string: the values above are the convention supported by the symptoms pipeline. Other values are accepted and persisted, but will not trigger downstream rules.

additional_info

FieldTypeUnitRangeDescription
g_forcenumberg0.0 to 5.0G-force measured during the event.
speed_at_eventnumberkm/h0.0 to 250.0Asset speed at the moment of the event.
cargo_weightnumberkg0.0 to 20000.0Cargo weight at the moment of the event, if the device reports it.

Prefer events[] for data already typed by the provider (hard brake, hard acceleration, hard turn). If you only have the raw accelerometer or gyroscope reading, send it in sensors.acceleration / sensors.gyroscope and let the pipeline derive the event.


Response

Success (200 OK)

{ "status": "success", "message": "Message sent successfully" }

Validation Error (400 Bad Request)

{ "status": "validation_error", "message": "Request validation failed", "errors": { "assetId": "Asset ID is required and cannot be blank", "timestamp": "Timestamp is required" } }

Unauthorized (401)

Returned when the token is invalid or expired.

Internal Error (500)

{ "status": "error", "message": "Failed to publish message", "detail": "Connection timeout" }

Postman Collection

Download our ready-to-use Postman collection with all examples:
Download Postman Collection

The collection includes:

  • Authentication - Pre-configured Auth0 login flow
  • Track with GPS and Metrics - Basic OBD2 metrics example
  • Track with Batched Metrics - Multiple timestamped metrics
  • Track with Full Telemetry - Comprehensive example with all fields
  • Track with DTC Codes - Diagnostic trouble codes example
  • Track with Provider Events - Typed events (hard_brake, hard_accel, hard_turn)

Complete Examples

Minimal Request (Required Fields Only)

# Get token first TOKEN=$(curl -s --request POST \ --url https://api.microboxlabs.com/api/v1/login \ --header 'Content-Type: application/json' \ --data '{ "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", "audience": "https://iot.streamhub.cl/v1/asset/track", "grant_type": "client_credentials" }' | jq -r '.access_token') # Send tracking data curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-001' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z" }'

With GPS Location

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-002' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "type": "Truck", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 65.5, "heading": 180 } }'

With Canonical Metrics (OBD-II/J1939)

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-003' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "type": "Truck", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 65.5 }, "metrics": { "schema": "miot.metrics@1.0", "items": [ { "k": "engine.rpm", "v": 2500, "u": "rpm", "src": "obd2", "q": "ok" }, { "k": "vehicle.speed", "v": 72, "u": "km/h", "src": "obd2", "q": "ok" }, { "k": "engine.coolant_temp", "v": 85, "u": "C", "src": "obd2", "q": "ok" }, { "k": "fuel.level", "v": 65, "u": "%", "src": "obd2", "q": "ok" }, { "k": "battery.voltage", "v": 14.2, "u": "V", "src": "obd2", "q": "ok" } ], "seq": 1001 } }'

With Batched Metrics (Multiple Timestamps)

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-004' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:30Z", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 58 }, "metrics": { "schema": "miot.metrics@1.0", "items": [ { "k": "engine.rpm", "v": 1800, "u": "rpm", "ts": "2024-01-15T10:30:10Z", "src": "obd2" }, { "k": "engine.rpm", "v": 1950, "u": "rpm", "ts": "2024-01-15T10:30:20Z", "src": "obd2" }, { "k": "engine.rpm", "v": 2100, "u": "rpm", "ts": "2024-01-15T10:30:30Z", "src": "obd2" }, { "k": "vehicle.speed", "v": 52, "u": "km/h", "ts": "2024-01-15T10:30:10Z", "src": "obd2" }, { "k": "vehicle.speed", "v": 55, "u": "km/h", "ts": "2024-01-15T10:30:20Z", "src": "obd2" }, { "k": "vehicle.speed", "v": 58, "u": "km/h", "ts": "2024-01-15T10:30:30Z", "src": "obd2" } ], "seq": 1002, "device_ts": "2024-01-15T10:30:30Z" } }'

With DTC Codes (Diagnostic Trouble Codes)

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-005' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 45 }, "metrics": { "schema": "miot.metrics@1.0", "items": [ { "k": "engine.rpm", "v": 2400, "u": "rpm", "src": "obd2", "q": "ok" }, { "k": "engine.coolant_temp", "v": 105, "u": "C", "src": "obd2", "q": "ok" }, { "k": "dtc.mil_on", "v": true, "src": "obd2", "q": "ok" }, { "k": "dtc.count", "v": 2, "src": "obd2", "q": "ok" }, { "k": "dtc.codes", "v": ["P0217", "P0128"], "src": "obd2", "q": "ok", "meta": { "descriptions": { "P0217": "Engine Coolant Over Temperature Condition", "P0128": "Coolant Thermostat Below Regulating Temperature" } } } ], "seq": 1003 } }'

With GPS-Connected Sensors (No OBD-II/CAN BUS)

For GPS devices that provide odometer and engine status directly:

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-004' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 65.5, "heading": 180 }, "sensors": { "odometer": 125432.5, "engine_status": 1 } }'

With Accelerometer and Gyroscope (IMU Sensors)

For GPS devices equipped with an inertial measurement unit that reports acceleration and rotation:

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-006' \ --header 'X-Request-Timestamp: 1705315800' \ --data '{ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 65.5, "heading": 180 }, "sensors": { "acceleration": { "x_axis": 1.5, "y_axis": -0.5, "z_axis": 2.0, "g_force": 1.2 }, "gyroscope": { "rotation_rate_x": 5.5, "rotation_rate_y": -3.2, "rotation_rate_z": 1.8 }, "odometer": 125432.5, "engine_status": 1 } }'

With Provider Events (Hard Brake/Accel/Turn)

For GPS devices or providers that pre-compute discrete driving events and send them typed — with severity and threshold — inside the events[] array.

curl --request POST \ --url https://iot.streamhub.cl/v1/asset/track \ --header "Authorization: Bearer $TOKEN" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Request-Id: req-007' \ --header 'X-Request-Timestamp: 1740492537' \ --data '{ "asset_id": "TTBJ72", "timestamp": "2026-02-13T13:48:57Z", "gps": { "latitude": -29.810218, "longitude": -71.282617, "speed": 74.5, "heading": 315 }, "events": [ { "event_type": "hard_brake", "timestamp": "2026-02-13T13:48:55Z", "severity": "high", "threshold_exceeded": true, "additional_info": { "g_force": 0.82, "speed_at_event": 74.5, "cargo_weight": 5000 } }, { "event_type": "hard_accel", "timestamp": "2026-02-13T13:48:40Z", "severity": "medium", "threshold_exceeded": true, "additional_info": { "g_force": 0.61, "speed_at_event": 42.0 } }, { "event_type": "hard_turn", "timestamp": "2026-02-13T13:48:30Z", "severity": "medium", "threshold_exceeded": true, "additional_info": { "g_force": 0.55, "speed_at_event": 58.0 } } ] }'

Node.js (fetch)

const res = await fetch("https://iot.streamhub.cl/v1/asset/track", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", Accept: "application/json", "X-Request-Id": crypto.randomUUID(), "X-Request-Timestamp": String(Math.floor(Date.now() / 1000)), }, body: JSON.stringify({ asset_id: "TTBJ72", timestamp: "2026-02-13T13:48:57Z", gps: { latitude: -29.810218, longitude: -71.282617, speed: 74.5, heading: 315 }, events: [ { event_type: "hard_brake", timestamp: "2026-02-13T13:48:55Z", severity: "high", threshold_exceeded: true, additional_info: { g_force: 0.82, speed_at_event: 74.5, cargo_weight: 5000 }, }, { event_type: "hard_accel", timestamp: "2026-02-13T13:48:40Z", severity: "medium", threshold_exceeded: true, additional_info: { g_force: 0.61, speed_at_event: 42.0 }, }, { event_type: "hard_turn", timestamp: "2026-02-13T13:48:30Z", severity: "medium", threshold_exceeded: true, additional_info: { g_force: 0.55, speed_at_event: 58.0 }, }, ], }), }); if (!res.ok) throw new Error(`asset/track ${res.status}: ${await res.text()}`);

Python (requests)

import time, uuid, requests res = requests.post( "https://iot.streamhub.cl/v1/asset/track", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", "Accept": "application/json", "X-Request-Id": str(uuid.uuid4()), "X-Request-Timestamp": str(int(time.time())), }, json={ "asset_id": "TTBJ72", "timestamp": "2026-02-13T13:48:57Z", "gps": {"latitude": -29.810218, "longitude": -71.282617, "speed": 74.5, "heading": 315}, "events": [ { "event_type": "hard_brake", "timestamp": "2026-02-13T13:48:55Z", "severity": "high", "threshold_exceeded": True, "additional_info": {"g_force": 0.82, "speed_at_event": 74.5, "cargo_weight": 5000}, }, { "event_type": "hard_accel", "timestamp": "2026-02-13T13:48:40Z", "severity": "medium", "threshold_exceeded": True, "additional_info": {"g_force": 0.61, "speed_at_event": 42.0}, }, { "event_type": "hard_turn", "timestamp": "2026-02-13T13:48:30Z", "severity": "medium", "threshold_exceeded": True, "additional_info": {"g_force": 0.55, "speed_at_event": 58.0}, }, ], }, timeout=10, ) res.raise_for_status()

Java (HttpClient)

import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Instant; import java.util.UUID; String body = """ { "asset_id": "TTBJ72", "timestamp": "2026-02-13T13:48:57Z", "gps": { "latitude": -29.810218, "longitude": -71.282617, "speed": 74.5, "heading": 315 }, "events": [ { "event_type": "hard_brake", "timestamp": "2026-02-13T13:48:55Z", "severity": "high", "threshold_exceeded": true, "additional_info": { "g_force": 0.82, "speed_at_event": 74.5, "cargo_weight": 5000 } }, { "event_type": "hard_accel", "timestamp": "2026-02-13T13:48:40Z", "severity": "medium", "threshold_exceeded": true, "additional_info": { "g_force": 0.61, "speed_at_event": 42.0 } }, { "event_type": "hard_turn", "timestamp": "2026-02-13T13:48:30Z", "severity": "medium", "threshold_exceeded": true, "additional_info": { "g_force": 0.55, "speed_at_event": 58.0 } } ] } """; HttpRequest req = HttpRequest.newBuilder(URI.create("https://iot.streamhub.cl/v1/asset/track")) .header("Authorization", "Bearer " + token) .header("Content-Type", "application/json") .header("Accept", "application/json") .header("X-Request-Id", UUID.randomUUID().toString()) .header("X-Request-Timestamp", String.valueOf(Instant.now().getEpochSecond())) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); if (res.statusCode() >= 300) throw new RuntimeException("asset/track " + res.statusCode() + ": " + res.body());

Python Example

import requests import time from microboxlabs_auth_manager import AuthToken # Initialize auth auth = AuthToken( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", audience="https://iot.streamhub.cl/v1/asset/track", grant_type="client_credentials" ) # Send tracking data with metrics response = requests.post( "https://iot.streamhub.cl/v1/asset/track", headers={ "Authorization": f"Bearer {auth.get_token()}", "Content-Type": "application/json", "Accept": "application/json", "X-Request-Id": f"req-{int(time.time())}", "X-Request-Timestamp": str(int(time.time())) }, json={ "asset_id": "A123456789", "timestamp": "2024-01-15T10:30:00Z", "gps": { "latitude": -33.4489, "longitude": -70.6693, "speed": 65.5 }, "metrics": { "schema": "miot.metrics@1.0", "items": [ {"k": "engine.rpm", "v": 2500, "u": "rpm", "src": "obd2", "q": "ok"}, {"k": "vehicle.speed", "v": 72, "u": "km/h", "src": "obd2", "q": "ok"}, {"k": "engine.coolant_temp", "v": 85, "u": "C", "src": "obd2", "q": "ok"}, {"k": "fuel.level", "v": 65, "u": "%", "src": "obd2", "q": "ok"} ], "seq": 1001 } } ) print(response.json())

Common Metrics

Quick reference for frequently used canonical metrics:

KeyDescriptionUnitSource
engine.rpmEngine speedrpmOBD PID 0C
vehicle.speedVehicle speedkm/hOBD PID 0D
fuel.levelFuel tank level%OBD PID 2F
engine.coolant_tempCoolant temperatureCOBD PID 05
engine.loadCalculated engine load%OBD PID 04
battery.voltageBattery/system voltageVOBD PID 42
vehicle.odometerTotal distancekmOBD PID 31
throttle.positionThrottle opening%OBD PID 11
dtc.mil_onCheck engine light statusboolOBD PID 01
dtc.codesActive trouble codesarrayOBD Mode 03

→ See Reference > Metrics Registry for the complete catalog of canonical metrics.


Error Handling

Retry Logic

Implement exponential backoff for transient errors:

import time import requests def send_with_retry(url, headers, data, max_retries=3): for attempt in range(max_retries): try: response = requests.post(url, headers=headers, json=data) if response.status_code == 200: return response.json() elif response.status_code == 401: # Token expired - refresh and retry headers["Authorization"] = f"Bearer {get_new_token()}" elif response.status_code >= 500: # Server error - retry with backoff time.sleep(2 ** attempt) else: # Client error - don't retry return response.json() except requests.exceptions.RequestException: time.sleep(2 ** attempt) raise Exception("Max retries exceeded")

Validation Errors

When you receive a validation error, check the errors object for specific field issues:

{ "status": "validation_error", "message": "Request validation failed", "errors": { "assetId": "Asset ID is required and cannot be blank" } }

Fix the indicated fields and retry.

Last updated on