Appearance
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) | |
|---|---|---|
| For | Operators and customers: the values Mission Control shows and stores. | Your engineers and Urban Sky support: diagnostics, errors, counters, temperatures. |
| Where it goes | POST /payloads/telemetry on Mission Control, over HTTPS. | The Husky bus at port 4318 on the payload LAN. The bus forwards it to Urban Sky. |
| Credentials | x-device-api-token. | None. The bus knows your payload from its address on the payload LAN. |
| Shape | Fields 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 in | Mission Control, and GET /payloads/:id/telemetry. | Urban Sky's monitoring and support tools. Not in Mission Control. |
| Filtering | Every 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 bus | Yes. | No. It needs a Husky bus. |
| Client | Your 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
| Field | Type | Meaning |
|---|---|---|
sensor.temperature_c | Number, −80 to 125 °C | Measured temperature. |
sensor.sample_period_s | Number, 1 to 60 seconds | Applied sampling interval. |
sensor.state | idle, sampling, or fault | Current 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.jsonExpected: 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:
| Field | Meaning |
|---|---|
accepted | Samples stored. |
rejected | Invalid sample sequence numbers and reasons. |
decimated | Samples discarded by the declared persistence interval. |
dropped | Samples beyond the batch limit. |
failed | Persistence errors. |
Reporting rules
- Send a flat
valuesmap 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
seqfor 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
undeclaredflag. Omission does not clear an older UI value; report a fault insensor.statewhen 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.jsonExpected: 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 --followbalto 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.