Asset Track
Send telemetry data for an asset to ModularIoT.
Endpoint
POST https://iot.streamhub.cl/v1/asset/trackAuthentication
Requires a valid Bearer token with asset:track:write scope.
Authorization: Bearer <access_token>Request
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <access_token> |
Content-Type | Yes | application/json |
Accept | Recommended | application/json for JSON responses |
X-Request-Id | Yes | Unique identifier for request tracing |
X-Request-Timestamp | Yes | Request 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
| Field | Type | Required | Description |
|---|---|---|---|
asset_id | string | Yes | Unique identifier for the asset |
timestamp | string | Yes | ISO 8601 timestamp with timezone |
type | string | No | Asset type (e.g., “Truck”, “Container”) |
owner | string | No | Asset owner name |
year | integer | No | Year of manufacture |
gps | object | No | GPS location data |
telecom | object | No | Connectivity information |
sensors | object | No | Sensor readings |
driver_info | object | No | Driver information |
co_driver_info | object | No | Co-driver information |
peripherals | object | No | Connected peripherals |
metrics | object | No | Canonical metrics (miot.metrics@1.0) |
events | array | No | Discrete events |
GPS Object
{
"latitude": -33.4489,
"longitude": -70.6693,
"altitude": 520,
"speed": 65.5,
"heading": 180,
"accuracy": 5
}| Field | Type | Unit | Description |
|---|---|---|---|
latitude | number | degrees | WGS84 latitude (-90 to 90) |
longitude | number | degrees | WGS84 longitude (-180 to 180) |
altitude | number | meters | Altitude above sea level |
speed | number | km/h | Ground speed |
heading | number | degrees | Direction (0-360) |
accuracy | number | meters | GPS 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
| Field | Type | Required | Description |
|---|---|---|---|
schema | string | Yes | Must be "miot.metrics@1.0" |
items | array | Yes | Array of MetricItem objects (min 1, max 200) |
seq | integer | No | Device sequence number for ordering/deduplication |
device_ts | string | No | Device clock timestamp (RFC3339) |
capabilities | object | No | Opaque metadata about supported metrics |
MetricItem Fields
| Field | Type | Required | Description |
|---|---|---|---|
k | string | Yes | Canonical metric key (e.g., engine.rpm) |
v | any | Yes | Value (number, boolean, string, object, or array) |
u | string | No | Canonical unit (e.g., rpm, km/h, C, V, %) |
ts | string | No | Timestamp override for this item (RFC3339) |
src | string | No | Source: obd2, j1939, can, oem, derived, unknown |
q | string | No | Quality: ok, estimated, stale, invalid, missing |
meta | object | No | Opaque 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):
{
"odometer": 125432.5,
"engine_status": 1
}| Field | Type | Description |
|---|---|---|
odometer | number | Total distance traveled in kilometers |
engine_status | integer | Engine state: 1 = on, 0 = off |
Use this object when the GPS device provides odometer and engine status readings directly, without relying on the vehicle’s diagnostic bus.
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
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
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
}
}'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:
| Key | Description | Unit | Source |
|---|---|---|---|
engine.rpm | Engine speed | rpm | OBD PID 0C |
vehicle.speed | Vehicle speed | km/h | OBD PID 0D |
fuel.level | Fuel tank level | % | OBD PID 2F |
engine.coolant_temp | Coolant temperature | C | OBD PID 05 |
engine.load | Calculated engine load | % | OBD PID 04 |
battery.voltage | Battery/system voltage | V | OBD PID 42 |
vehicle.odometer | Total distance | km | OBD PID 31 |
throttle.position | Throttle opening | % | OBD PID 11 |
dtc.mil_on | Check engine light status | bool | OBD PID 01 |
dtc.codes | Active trouble codes | array | OBD 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.