Skip to content

Report telemetry ​

Telemetry, or logs and metrics? ​

A payload has two ways to send data. Use telemetry for what operators see. Use logs and metrics for what engineers need to debug the payload.

Telemetry (this page)Logs and metrics (Run on a Husky bus)
ForOperators and customers: the values Mission Control shows and stores.Your engineers and Urban Sky support: diagnostics, errors, counters, temperatures.
Where it goesPOST /payloads/telemetry on Mission Control, over HTTPS.The Husky bus at port 4318 on the payload LAN. The bus forwards it to Urban Sky.
Credentialsx-device-api-token.None. The bus knows your payload from its address on the payload LAN.
ShapeFields you declare in the telemetry manifest, with units and ranges.Free-form log lines with an optional event and attributes. Declared gauges and counters.
Shown inMission Control, and GET /payloads/:id/telemetry.Urban Sky's monitoring and support tools. Not in Mission Control.
FilteringEvery accepted sample is kept, unless you set minMessageIntervalMs.warn and error lines, and lines with an event, leave the bus. Other lines stay on the bus.
Works without a busYes.No. It needs a Husky bus.
ClientYour own HTTP client.PayloadTelemetry in the Python or JavaScript SDK, an OpenTelemetry exporter, or plain HTTP.

Do not send operator values as metrics only: Mission Control never shows metrics. Do not send debug output as telemetry: telemetry fields are part of your manifest, and operators see them.

Before you start ​

Complete Connect. Keep MC_URL, DEVICE_API_TOKEN, PAYLOAD_ID, and announce.json. Set OPERATOR_TOKEN for readback.

1. Understand the fields you declared ​

FieldTypeMeaning
sensor.temperature_cNumber, −80 to 125 °CMeasured temperature.
sensor.sample_period_sNumber, 1 to 60 secondsApplied sampling interval.
sensor.stateidle, sampling, or faultCurrent state.

Define unique keys, types, labels, and units from the device's actual capabilities.

Set staleAfterS above the reporting interval plus expected delay. The example uses 30 seconds; adjust it if the reporting interval increases.

Omit minMessageIntervalMs to retain every accepted sample. Setting it asks MC to discard closely spaced samples; it does not control device sampling.

2. Send one measurement ​

Replace the example values with readings taken at the supplied timestamp:

bash
PAYLOAD_SERIAL=$(jq -er '.identity.serial' announce.json) &&
jq -n --arg serial "$PAYLOAD_SERIAL" \
  --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '{
  serial: $serial,
  schemaVersion: 1,
  samples: [{
    timestamp: $timestamp,
    seq: 1,
    values: {
      "sensor.temperature_c": 22.4,
      "sensor.sample_period_s": 2,
      "sensor.state": "sampling"
    }
  }]
}' > telemetry.json &&
curl --fail-with-body --silent --show-error \
  -X POST "${MC_URL:?Set MC_URL}/payloads/telemetry" \
  -H "x-device-api-token: ${DEVICE_API_TOKEN:?Set DEVICE_API_TOKEN}" \
  -H 'Content-Type: application/json' \
  --data-binary @telemetry.json

Expected: HTTP 200 and one stored sample:

json
{
  "success": true,
  "accepted": 1,
  "rejected": [],
  "decimated": 0,
  "dropped": 0,
  "failed": 0
}

Check all response fields. HTTP 200 alone does not confirm storage:

FieldMeaning
acceptedSamples stored.
rejectedInvalid sample sequence numbers and reasons.
decimatedSamples discarded by the declared persistence interval.
droppedSamples beyond the batch limit.
failedPersistence errors.

Reporting rules ​

  • Send a flat values map with native JSON numbers, strings, and booleans.
  • Use the actual RFC 3339 UTC sample time, including for buffered data. Missing/invalid times or times more than five minutes ahead of MC are rejected.
  • Increase seq for each sample. Persist it if continuity across reboot matters. MC does not deduplicate samples by sequence number.
  • Omit unavailable readings. A wrong type, invalid enum, or out-of-range value rejects the whole sample. Undeclared keys are stored with an undeclared flag. Omission does not clear an older UI value; report a fault in sensor.state when appropriate.
  • Match the registered manifest's schemaVersion. Re-announce before changing versions; handle old buffered samples separately.
  • Keep batches within the limits. For binary telemetry, see protobuf.

Telemetry works before operator adoption.

3. Keep the address current ​

Send a heartbeat every 15 seconds to start. Read the serial and address from announce.json:

bash
PAYLOAD_SERIAL=$(jq -er '.identity.serial' announce.json) &&
PAYLOAD_ADDRESS=$(jq -er '.reachability.address' announce.json) &&
jq -n --arg serial "$PAYLOAD_SERIAL" \
  --arg address "$PAYLOAD_ADDRESS" \
  --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  '{serial: $serial, address: $address, timestamp: $timestamp}' > heartbeat.json &&
curl --fail-with-body --silent --show-error \
  -X POST "${MC_URL:?Set MC_URL}/payloads/heartbeat" \
  -H "x-device-api-token: ${DEVICE_API_TOKEN:?Set DEVICE_API_TOKEN}" \
  -H 'Content-Type: application/json' \
  --data-binary @heartbeat.json

Expected: HTTP 200 with id, serial, and address. Re-announce to change the port, protocol, or manifests.

4. Read the measurement back ​

With Balto. Watch what your payload sends, as MC receives it:

bash
balto trace ENV-001 --up --follow

balto trace takes a payload serial or a bus. --up shows only what the bus and payloads sent. It prints one row per source each 30 seconds with a count; --every prints one row per telemetry message. In the Balto app, the bus's Payloads tab shows your payload's latest values.

With the HTTP API. Use an operator token with mission-control:payload:read in the same organization:

bash
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${OPERATOR_TOKEN:?Set OPERATOR_TOKEN}" \
  "${MC_URL:?Set MC_URL}/payloads/${PAYLOAD_ID:?Run Connect first}/telemetry"

Check: the response is an array; compare each message’s time and data.values with your sample. This endpoint returns the latest 50 samples from the last seven days of sample time. Add ?limit= for more samples, up to 500. Older accepted data will not appear here.

If a sample does not appear, read the rejections that MC recorded for the device in the last seven days:

bash
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${OPERATOR_TOKEN:?Set OPERATOR_TOKEN}" \
  "${MC_URL:?Set MC_URL}/payloads/${PAYLOAD_ID:?Run Connect first}/ingest-rejections"

If it fails ​

  • 404: announce first using the same credential and serial.
  • accepted: 0: inspect rejection, decimation, overflow, and failure counts.
  • Fresh heartbeat but stale values: check the sampling loop.
  • More: Troubleshooting.

← Connect your device · Next: Implement commands →