Skip to content

Connect your device ​

This page is the self-announce path: your payload announces itself to MC. Most payloads should serve the device API instead, and let the bus announce them.

Before you start ​

Complete Set up a test bench. Your payload has an address on the bus's payload LAN and reaches the Internet. Payload Lab does not test the self-announce path yet; use it only for logs and metrics on this path.

Ask Urban Sky for the following:

ItemUsed for
Test API URLMC_URL, without a trailing slash.
Enrolled device tokenDEVICE_API_TOKEN; identifies the device's organization.
The bus's serialidentity.parentSerial, so MC reaches your command server through that bus. balto payloads list shows it.
An Urban Sky accountBalto, and the operator steps (adopt, enable commands, send). See Test with Balto.

Use Bash or zsh, curl 7.76+ (for --fail-with-body), and jq. Keep credentials out of committed files.

The operator steps in this guide use balto payloads, which signs in as you. If you call the HTTP API instead, set OPERATOR_TOKEN to a signed-in operator's access JWT in the same organization. An SDK/API token or the device token does not work for operator requests.

1. Understand the routes ​

RequestRouteAuthentication
Device → MCHTTPS to MC_URL, out through the bus's Internet link.x-device-api-token.
Operator → MCHTTPS to MC_URL, from Balto or your own client.Your Urban Sky sign-in, or a bearer token.
MC → deviceThrough the bus link to your address on the payload LAN. Your device needs no public address.The bus link is mutual TLS between MC and the bus. Add mTLS on your command server if you want it end to end.

Announce your address on the payload LAN (for example 192.168.8.20 on a Husky bus) and name the bus in identity.parentSerial. MC finds your lease on that bus and dials your command server through the bus link. Bind the command server to the payload-LAN interface.

Loopback, unspecified, link-local/metadata, and multicast addresses are rejected. Other network modes, for a payload that does not fly on a Husky bus, are in the API reference.

2. Create the first announcement ​

Save as announce.json. Replace the identity and address. Start with telemetry only; add commands after their handlers work.

json
{
  "schemaVersion": 1,
  "identity": {
    "serial": "ENV-001",
    "name": "Environmental sensor",
    "vendor": "Example Instruments",
    "parentSerial": "uskybus-8901fc"
  },
  "reachability": {
    "address": "192.168.8.20",
    "port": 8091,
    "protocol": "http",
    "networkMode": "NETWORK_MODE_SHARED_VPN"
  },
  "codec": "CODEC_JSON",
  "security": "SECURITY_TRANSPORT_VPN",
  "telemetryManifest": {
    "schemaVersion": 1,
    "staleAfterS": 30,
    "fields": [
      {
        "key": "sensor.temperature_c",
        "label": "Temperature",
        "type": "FIELD_TYPE_NUMBER",
        "unit": "°C",
        "min": -80,
        "max": 125
      },
      {
        "key": "sensor.sample_period_s",
        "label": "Applied sample period",
        "type": "FIELD_TYPE_NUMBER",
        "unit": "s",
        "min": 1,
        "max": 60
      },
      {
        "key": "sensor.state",
        "label": "Sensor state",
        "type": "FIELD_TYPE_ENUM",
        "values": ["idle", "sampling", "fault"]
      }
    ]
  },
  "commandManifest": {
    "schemaVersion": 1,
    "commands": []
  }
}

Keep the serial stable across restarts: 1–64 letters, digits, _, ., :, or -. Use the exact property names and enum strings shown. Every announcement needs at least one telemetry field or command.

Set address to the address your payload has on the payload LAN, and parentSerial to your bus's serial. On a cloud bench bus the address is 10.55.0.x. NETWORK_MODE_SHARED_VPN is the mode for a payload behind a bus; the name is older than the bus link. With SECURITY_TRANSPORT_VPN and "protocol": "http", your command server needs no certificate: the bus link carries the command with mutual TLS as far as the bus. For end-to-end mTLS, use SECURITY_TRANSPORT_MTLS and "protocol": "https", and ask Urban Sky for a device certificate.

Re-announce when DHCP gives your payload a new address.

Optional: say what the device is ​

Without these fields, MC shows identity.name. Add them to identity to give operators a clear label:

json
"identity": {
  "serial": "ENV-001",
  "name": "Environmental sensor",
  "vendor": "Example Instruments",
  "displayName": "ENV-001 sensor",
  "kind": "env-sensor",
  "model": "ENV-1",
  "softwareVersion": "1.4.2",
  "hardwareRevision": "rev C",
  "description": "Temperature sensor with a configurable sampling interval"
}
FieldStored limit
displayName, model80 characters.
kind32 characters: lowercase letters, digits, _, and -.
softwareVersion, hardwareRevision64 characters.
description200 characters.

MC trims and shortens values that are too long. MC drops a kind with other characters. It does not reject the announcement. These fields are labels only. They do not change permissions or routing. Update softwareVersion with each release.

Optional: declare other network services ​

If the device serves more than its command server and video, list each service in services, for example an SSH server:

json
"services": [{ "name": "ssh", "port": 22, "protocol": "ssh" }]

Use up to 16 services. Each name is unique and matches ^[a-z0-9][a-z0-9._-]{0,31}$. A declaration grants no access by itself. Send the full list in every announcement. An announcement without services removes the stored list.

3. Send the announcement ​

Replace the placeholders below. Use the API origin, without a trailing slash. Stop at any error before continuing.

bash
MC_URL='https://YOUR_API_HOST'
DEVICE_API_TOKEN='YOUR_ENROLLED_DEVICE_TOKEN'
OPERATOR_TOKEN='YOUR_OPERATOR_ACCESS_JWT'

Read the serial from your announcement and submit it:

bash
PAYLOAD_SERIAL=$(jq -er '.identity.serial' announce.json) &&
curl --fail-with-body --silent --show-error \
  -X POST "${MC_URL:?Set MC_URL}/payloads/announce" \
  -H "x-device-api-token: ${DEVICE_API_TOKEN:?Set DEVICE_API_TOKEN}" \
  -H 'Content-Type: application/json' \
  --data-binary @announce.json > announce-response-next.json &&
PAYLOAD_ID=$(jq -er '.id' announce-response-next.json) &&
mv announce-response-next.json announce-response.json &&
jq . announce-response.json

Expected: HTTP 200 with a UUID id, serial matching your announcement, status: "discovered", parent_serial: null, and relays: null.

Re-announcing with the same credential and serial updates the address and supplied manifests, preserving the UUID and adoption status. Another credential cannot claim that serial. If you already registered it, expect its existing status rather than discovered.

commandsEnabled, telemetrySchemaUrl, and commandsUrl belong to operator create/update requests, not announcements.

Keep announce.json, announce-response.json, and the shell variables for the next page. In a new shell, recover the UUID with PAYLOAD_ID=$(jq -er '.id' announce-response.json).

Check it worked ​

List the bus's payloads in Balto. Your serial appears with status: "discovered":

bash
balto payloads list --bus uskybus-8901fc

In the Balto app (balto ui), open the bus and choose the Payloads tab. Your payload is listed under the bus.

If it fails ​

  • 401: check the device token and API URL.
  • 403: ask Urban Sky to resolve the existing registration's ownership.
  • 400: check the address, field names, enum values, and manifests.
  • Not in balto payloads list: check that you used the same organization's device token, and that parentSerial is the bus's serial. See Troubleshooting.

← Set up a test bench · Next: Report telemetry →