Control device MQTT flow

MQTT is a supported broker transport for control devices that should keep a connection open. Firmware starts with the HTTPS API: authenticate, read the MQTT catalog, request a short-lived MQTT credential, then connect with the values returned by Realer.

OAuth token issue, catalog discovery, and MQTT credential issue are HTTPS calls. This page describes the MQTT runtime loop after the device has authenticated and received the returned CONNECT tuple.

Transport and security

The public broker endpoint uses MQTT over TLS: mqtts://mqtt.therealer.com:8883. Plain MQTT is not accepted for public device traffic.

Use the catalog broker_url for the broker connection. Use HTTPS only for OAuth, catalog discovery, and MQTT credential issue. The OAuth Bearer token is never the MQTT password.

Implementation references

This page summarizes the MQTT runtime sequence. OAuth, catalog discovery, and credential issue remain HTTPS calls; feed data, desired-state delivery, and application acknowledgements use MQTT over TLS after broker connection.

Step What firmware uses Firmware purpose
Authenticate OAuth device authentication Issue or renew the Bearer access token used for HTTPS bootstrap calls.
Read MQTT catalog GET /iot/v1/devices/{device_id}/mqtt Discover broker URL, MQTT limits, topics, scopes, and credential request data.
Request CONNECT tuple POST /iot/v1/mqtt-credentials Receive the short-lived MQTT client ID, username, and plaintext credential returned once.
Publish and subscribe Catalog feed_data_topic, feed_data_ack_topic, and desired_topic values Publish feed data, receive application acknowledgements, and receive command desired-state messages.
Handle application messages MQTT payload reference Use canonical JSON payloads plus acknowledgement status, retryable, and error.type for firmware behavior.
Handle field behavior Field semantics Use shared retry, ordering, desired-state correlation, expiry, and result-handling rules.

Authorization scopes

MQTT scopes are Realer OAuth authorization values. Request them in POST /oauth/token; they are not sent inside MQTT packets.

During HTTPS bootstrap, Realer uses the Bearer token scopes and the requested MQTT credential scopes to issue a short-lived CONNECT tuple. During broker use, Realer checks the scopes stored on that MQTT credential for CONNECT, SUBSCRIBE, and PUBLISH.

Scope Firmware use
iot:catalog:read Bearer token can read the MQTT catalog over HTTPS.
iot:mqtt:connect MQTT credential can connect to the broker.
iot:mqtt:ack:read MQTT credential can subscribe to the application acknowledgement topic.
iot:mqtt:feed-data:write MQTT credential can publish sensor values and command acknowledgements to catalog feed-data topics.
iot:mqtt:desired:read MQTT credential can subscribe to command desired-state topics. Omit this scope when the device has no command routes or does not handle desired-state messages.

Flow diagram

MQTT broker HTTPS API Device OAuth and catalog calls use HTTPS alt [Credentials available] [Credentials unavailable] opt [Command routes] loop [Before renew_after] POST /oauth/token OAuth scopes 200 OK Bearer token, renew_after GET /iot/v1/devices/{device_id}/mqtt 200 code 2000 broker, topics, endpoint POST /iot/v1/mqtt-credentials requested scopes 201 Created CONNECT tuple No credential_endpoint HTTPS if enabled CONNECT over MQTT/TLS returned tuple CONNACK SUBSCRIBE feed_data_ack_topic SUBSCRIBE desired_topic PUBLISH desired id, value, seq PUBLISH feed_data_topic message_id, value, time PUBACK broker accepted PUBLISH ack accepted/replayed/etc. POST /iot/v1/mqtt-credentials 201 Created new tuple Reconnect resubscribe topics

Firmware loop

  1. Get an OAuth access token
    Call POST /oauth/token with grant_type=client_credentials. Request iot:catalog:read and the MQTT scopes your firmware needs.
  2. Read the MQTT catalog
    Call GET /iot/v1/devices/{device_id}/mqtt with Authorization: Bearer <access_token>. This response tells the device which broker URL, client ID, topics, limits, and credential request to use.
  3. Request the MQTT CONNECT tuple
    Call the catalog credential_endpoint with its returned body unless your firmware intentionally requests a narrower scope set. The response returns mqtt_client_id, mqtt_username, mqtt_credential, scopes, expires_in, renew_after, and session_hard_expires_in.
  4. Connect with MQTT over TLS
    Connect to broker_url with the exact client ID, username, and password returned by Realer. The OAuth Bearer token is not used as the MQTT password.
  5. Use only catalog topics
    Subscribe to feed_data_ack_topic. Subscribe to command desired_topic values when your catalog contains command routes. Publish measurements and command acknowledgements to route feed_data_topic values.
  6. Renew before expiry
    Use renew_after to request the next MQTT credential, reconnect with the new tuple, and resubscribe.

Read the MQTT catalog

The OAuth response does not include the MQTT broker host, topics, username, or password. Read the catalog after authentication.

If the catalog has no credential_endpoint, MQTT credentials are not available for that device yet. Use the HTTPS flow only when HTTPS access is enabled for the same device; otherwise retry later and keep local safe behavior until MQTT credentials become available.
cURL
curl "https://api.therealer.com/iot/v1/devices/12345/mqtt" \
  -H 'Authorization: Bearer b11db7f6c816568eb3b156df3aeaa5'
GET /iot/v1/devices/{device_id}/mqtt

Return the MQTT broker URL, route topics, limits, credential lifecycle, and default credential request body.

Request headers
Authorization
(required)

The Bearer access token obtained from the OAuth token endpoint.

Type: String

Example: Bearer b11db7f6c816568eb3b156df3aeaa5

Path parameters
device_id
(required)

ID (id) of the authenticated control device that is reading its MQTT catalog.

Type: Integer

Example: 12345

Responses

200 OK - Abbreviated response example

Save the returned broker URL, client ID, acknowledgement topic, route topics, limits, credential lifecycle, and credential request body.

JSON
{
  "code": 2000,
  "mqtt": {
    "broker_url": "mqtts://mqtt.therealer.com:8883",
    "protocol": "mqtt-5.0",
    "client_id": "cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa",
    "qos": 1,
    "retain_policy": "disabled",
    "feed_data_ack_topic": "realer/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/acks",
    "feed_data_ack_subscribe_scope": "iot:mqtt:ack:read",
    "credential_endpoint": {
      "endpoint": "/iot/v1/mqtt-credentials",
      "http_method": "POST",
      "body": {
        "device_id": 12345,
        "scopes": [
          "iot:mqtt:connect",
          "iot:mqtt:ack:read",
          "iot:mqtt:feed-data:write"
        ]
      }
    }
  },
  "mqtt_routes": []
}

The live catalog can include limits, credential lifecycle hints, capabilities, and diagnostic fields. Firmware should honor limits it understands and ignore unknown fields.

Request the CONNECT tuple

Use the credential request from the catalog as the default request body. The response is sent with 201 Created, Cache-Control: no-store, and Pragma: no-cache. The password is returned once, so keep it only in short-lived runtime memory.

cURL
curl "https://api.therealer.com/iot/v1/mqtt-credentials" \
  -H 'Authorization: Bearer b11db7f6c816568eb3b156df3aeaa5' \
  -H 'Content-Type: application/json' \
  -d '{"device_id":12345,"scopes":["iot:mqtt:connect","iot:mqtt:ack:read","iot:mqtt:feed-data:write"]}'
POST /iot/v1/mqtt-credentials

Issue a short-lived MQTT CONNECT tuple for the authenticated control device.

Request headers
Authorization
(required)

The Bearer access token obtained from the OAuth token endpoint.

Type: String

Example: Bearer b11db7f6c816568eb3b156df3aeaa5

Content-Type
(required)

The request body media type.

Value: application/json

Request body
JSON
{
  "device_id": 12345,
  "scopes": [
    "iot:mqtt:connect",
    "iot:mqtt:ack:read",
    "iot:mqtt:feed-data:write"
  ]
}
Fields
device_id
(required)

ID of the authenticated control device that will connect to the broker.

scopes
(optional)

MQTT broker scopes requested for the short-lived CONNECT tuple. If omitted, Realer uses the default MQTT credential scopes for the device and requires iot:mqtt:connect.

Responses

201 Created

Use the returned client ID, username, and plaintext credential for MQTT CONNECT. The plaintext credential is returned once.

JSON
{
  "code": 2000,
  "mqtt": {
    "mqtt_client_id": "cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa",
    "mqtt_username": "realer-cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa-cred-v12",
    "mqtt_credential": "plaintext-returned-once",
    "scopes": [
      "iot:mqtt:connect",
      "iot:mqtt:ack:read",
      "iot:mqtt:feed-data:write"
    ],
    "expires_in": 3600,
    "renew_after": 2700,
    "session_hard_expires_in": 3600
  }
}
Do not log MQTT credentials. Do not store plaintext MQTT credentials in long-lived storage. Reconnect with a new tuple before expiry.

The response can include additional diagnostic fields. Firmware should use the documented fields above and ignore unknown fields.

Sensor routes and command routes

Sensor routes and command routes use the same feed-data publish shape. Command routes add an optional desired-state subscription.

Publishing command feed data reports the physical command state observed by the device. It does not directly update the desired command value stored by Realer.

Route type Firmware publishes Firmware subscribes
sensor feed_data_topic with sensor value payloads feed_data_ack_topic
command feed_data_topic with command acknowledgement payloads feed_data_ack_topic and desired_topic
Route examples

          {
            "source_type": "sensor",
            "mqtt": {
              "feed_data_topic": "realer/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/routes/rte_aaaaaaaaaaaaaaaaaaaaaaaaaa/feed-data",
              "feed_data_publish_scope": "iot:mqtt:feed-data:write"
            }
          },
          {
            "source_type": "command",
            "mqtt": {
              "feed_data_topic": "realer/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/routes/rte_bbbbbbbbbbbbbbbbbbbbbbbbbb/feed-data",
              "feed_data_publish_scope": "iot:mqtt:feed-data:write",
              "desired_topic": "realer/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/routes/rte_bbbbbbbbbbbbbbbbbbbbbbbbbb/desired",
              "desired_subscribe_scope": "iot:mqtt:desired:read"
            }
          }
        

Publish and acknowledgements

Publish feed data with QoS 1 and retain disabled. MQTT PUBACK means broker acceptance only; the application result arrives later on feed_data_ack_topic. See the MQTT payload reference for canonical JSON payloads.

  • message_id is required for retry and idempotency.
  • value is required.
  • value_received_at is required.
  • Command acknowledgements should include desired_id when responding to a desired-state message. Use report_status only when the outcome is not the default applied.
  • Command publishes without desired_id report local physical changes, such as a switch toggled at the device. For those local reports, omit report_status; if sent, only reported is valid.
  • After reconnecting, publish the latest physical command state with a new message_id if the device changed while offline.
  • Acknowledgement messages include version, status, retryable, message_id, and, when applicable, desired_id, the publish sequence_number, and error.
  • Acknowledgement statuses can be accepted, replayed, conflict, rejected, or error.

Runtime error handling

Condition Firmware handling
Broker disconnect Reconnect with the current valid tuple. If reconnect fails or the tuple is expired, request a new CONNECT tuple over HTTPS.
Credential expiry Use renew_after to renew before expiry. If renewal cannot complete, keep local safe behavior until cloud authorization is restored.
Publish failure Retry the same local event with the same message_id when the MQTT client reports that the publish was not accepted by the broker.
Negative acknowledgement Read acknowledgement status, retryable, and error.type. Retry only when retryable is true.
Desired-state rejection Publish command feed data with the same desired_id, current physical value, and report_status set to rejected or stale.

Desired-state messages

Command desired-state messages arrive on the route desired_topic. Each message has version, desired_id, value, valid_for_seconds, expires_at, and a server-assigned sequence_number. See the MQTT payload reference for the canonical payload shape.

  • valid_for_seconds is the execution window firmware should use for stale-command handling.
  • expires_at is diagnostic server time; devices with unreliable clocks should not depend on it alone.
  • Use sequence_number to ignore stale lower sequence values.
  • When a desired command is applied, publish command feed data with the same desired_id.
  • When a desired command is rejected or already stale, publish command feed data with report_status set to rejected or stale.

Credential renewal and shutdown

Use renew_after to start renewal before the MQTT credential expires. V1 uses controlled reconnect: request a new CONNECT tuple, disconnect the old MQTT session, reconnect with the new tuple, and resubscribe to acknowledgement and desired-state topics.

  • The previous tuple can be rejected for new CONNECT attempts after renewal.
  • On disconnect or reconnect, keep local safe behavior until cloud authorization is restored.
  • Call POST /oauth/revoke on intentional shutdown, decommissioning, or credential compromise when the device can still reach HTTPS.
  • Revoking the issuing OAuth token invalidates derived MQTT credentials.