Control device HTTPS flow

HTTPS is a supported direct request/response transport for firmware that polls Realer for catalog state and posts command acknowledgements or sensor readings through direct iot/v1 requests.

OAuth token issue and renewal happen over HTTPS. This page describes the HTTPS runtime loop after the device has authenticated.

Transport and security

The public device API is served from https://api.therealer.com/iot/v1/.... Plain HTTP is not accepted for public device traffic.

Implementation references

This page summarizes the HTTPS runtime sequence. The linked reference pages document request headers, parameters, examples, and response shapes.

Typical HTTPS firmware can omit the OAuth scope parameter; Realer then issues the default HTTPS scopes for catalog reads, desired-state reads, and feed-data writes. Use the authentication page when you need the full scope reference.

Step Endpoint or reference Firmware purpose
Authenticate OAuth device authentication Issue or renew the Bearer access token.
Bootstrap runtime GET /iot/v1/devices/{device_id}/bootstrap Read the HTTPS runtime descriptor, command and sensor catalogs, endpoint hints, cadence limits, and backoff metadata.
Targeted catalog refresh Device catalog Use granular command and sensor catalog endpoints when firmware needs to refresh only catalog data.
Poll desired state GET /iot/v1/devices/{device_id}/desired-states Read pending command desired states for HTTPS delivery.
Report state Feed-data ingestion Send command acknowledgements, local physical command changes, and sensor values.
Handle responses Response codes Use stable application-level code values for firmware behavior regardless of HTTP status grouping.
Handle field behavior Field semantics Use the shared rules for message_id, desired_id, report_status, expiry, and application results.

Flow diagram

HTTPS API Device Hardware ready Network online Bearer token Device-bound access alt [Token issued] [Rejected] loop [Authenticate until success] Apply desired states read sensors, check actuators alt [Created] [Replay] [Item rejected] opt [Batch upload] loop [HTTPS work loop] POST /oauth/token client credentials Validate client issue Bearer token 200 OK access_token, renew_after 400/401/429 OAuth error GET /iot/v1/devices/{device_id}/bootstrap Check token plan and cadence 200 code 2000 runtime, catalog, hints GET /iot/v1/devices/{device_id}/desired-states Check token plan and cadence 200 code 2000 desired_states[], poll hint POST /iot/v1/devices/{device_id}/feed-data source, value, time, message_id Validate JSON ingest or replay 201 created code 2000 200 idempotent code 2000 4xx/5xx error code + error.type POST /.../feed-data/batches items[] Validate envelope process in order 200 processed per-item results

Firmware loop

  1. Get an OAuth access token
    Call POST /oauth/token with grant_type=client_credentials. For a normal HTTPS-only device, omit scope or request iot:catalog:read iot:desired-state:read iot:feed-data:write.
  2. Read runtime bootstrap
    Call GET /iot/v1/devices/{device_id}/bootstrap with Authorization: Bearer <access_token>. The descriptor includes the command catalog, sensor catalog, desired-state polling hint, feed-data endpoints, QoS limits, and backoff metadata. The granular GET /iot/v1/devices/{device_id}/commands and GET /iot/v1/devices/{device_id}/sensors endpoints remain valid for targeted catalog reads.
  3. Poll desired state
    Call GET /iot/v1/devices/{device_id}/desired-states. Apply fresh desired states in sequence order and send a command feed-data acknowledgement with desired_id.
  4. Execute commands and collect sensor readings
    Treat desired-state items from Realer as command requests. Keep local safe behavior when authorization, network, or validation fails.
  5. Send feed data
    Call POST /iot/v1/devices/{device_id}/feed-data or POST /iot/v1/devices/{device_id}/feed-data/batches with command acknowledgements and sensor readings.
  6. Renew before expiry
    Use OAuth renew_after to request a new access token before expiry. If renewal fails, authenticate again before protected iot/v1 requests continue.

Runtime bootstrap

Bootstrap is the canonical startup call after OAuth. Use it to get the runtime descriptor in one read instead of fetching command and sensor catalogs separately during normal startup.

HTTPS devices receive HTTPS endpoint hints and QoS metadata. MQTT-capable devices can receive MQTT runtime data in the same descriptor; if a device only has MQTT runtime available, the response does not pretend HTTPS is available.

cURL
curl "https://api.therealer.com/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/bootstrap" \
  -H 'Authorization: Bearer b11db7f6c816568eb3b156df3aeaa5'
GET /iot/v1/devices/{device_id}/bootstrap

Return the authenticated control device runtime descriptor, including catalog data, runtime revisions, protocol-specific hints, cadence limits, and backoff metadata.

Request headers
Authorization
(required)

The Bearer access token obtained from the OAuth token endpoint.

Type: String

Example: Bearer b11db7f6c816568eb3b156df3aeaa5

Path parameters
device_id
(required)

Public id of the authenticated control device.

Type: String

Example: cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa

Responses

200 OK - HTTPS runtime

Use the returned descriptor to configure local firmware routing, polling, feed-data batching, and retry behavior.

JSON
{
  "code": 2000,
  "device": {
    "id": "cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa",
    "model_api_version": "2025-08-01",
    "supported_model_api_version": true
  },
  "runtime": {
    "version": "iot.v1.device_bootstrap",
    "api_version": "iot.v1",
    "generated_at": "2026-04-24T10:15:00.000000Z",
    "available_protocols": ["https"],
    "endpoints": {
      "bootstrap": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/bootstrap",
        "http_method": "GET",
        "required_scope": "iot:catalog:read"
      }
    },
    "oauth": {
      "token_type": "Bearer",
      "catalog_scope": "iot:catalog:read"
    },
    "backoff": {
      "retry_after_header": "Retry-After",
      "retryable_http_statuses": [429, 500, 502, 503, 504],
      "default_retry_after_seconds": 10
    },
    "catalog_revision": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "runtime_revision": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
  },
  "catalog": {
    "commands": [
      {
        "id": "cmd_bbbbbbbbbbbbbbbbbbbbbbbbbb",
        "value": "0.0",
        "value_type": "NumericRange",
        "kind": "relay",
        "pin_port": "6"
      }
    ],
    "sensors": [
      {
        "id": "sen_bbbbbbbbbbbbbbbbbbbbbbbbbb",
        "kind": "thermistor",
        "qc": "celsius_temperature",
        "mu": "degree_celsius:0",
        "pin_port": "5",
        "expected_update_interval_seconds": 60,
        "minimum_supported_update_interval_seconds": 30
      }
    ]
  },
  "https": {
    "protocol": "https",
    "profile": "https_polling",
    "endpoints": {
      "bootstrap": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/bootstrap",
        "http_method": "GET",
        "required_scope": "iot:catalog:read"
      },
      "commands": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/commands",
        "http_method": "GET",
        "required_scope": "iot:catalog:read"
      },
      "sensors": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/sensors",
        "http_method": "GET",
        "required_scope": "iot:catalog:read"
      }
    },
    "desired_states": {
      "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/desired-states",
      "http_method": "GET",
      "required_scope": "iot:desired-state:read",
      "poll_after_seconds": 10
    },
    "feed_data": {
      "single_endpoint": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/feed-data",
        "http_method": "POST",
        "required_scope": "iot:feed-data:write"
      },
      "batch_endpoint": {
        "endpoint": "/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/feed-data/batches",
        "http_method": "POST",
        "required_scope": "iot:feed-data:write"
      },
      "content_type": "application/json",
      "batch_items_key": "items"
    },
    "qos": {
      "minimum_request_interval_seconds": 10,
      "max_request_body_bytes": 32768
    }
  }
}

The granular command and sensor catalog endpoints, /desired-states, and feed-data endpoints remain valid. Bootstrap is the preferred startup contract because it gives firmware one revisioned runtime descriptor.

Desired-state polling

HTTPS desired state is a polling queue for command requests. The endpoint returns 200 OK even when the queue is empty so firmware can keep one stable loop.

cURL
curl "https://api.therealer.com/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/desired-states" \
  -H 'Authorization: Bearer b11db7f6c816568eb3b156df3aeaa5'
GET /iot/v1/devices/{device_id}/desired-states

Read pending HTTPS command desired states for the authenticated control device.

Request headers
Authorization
(required)

The Bearer access token obtained from the OAuth token endpoint.

Type: String

Example: Bearer b11db7f6c816568eb3b156df3aeaa5

Path parameters
device_id
(required)

Public id of the authenticated control device.

Type: String

Example: cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa

Responses

200 OK

Open desired states and the recommended polling cadence.

JSON
{
  "code": 2000,
  "desired_states": [
    {
      "desired_id": "des_aaaaaaaaaaaaaaaaaaaaaaaaaa",
      "device_id": "cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa",
      "command_id": "cmd_bbbbbbbbbbbbbbbbbbbbbbbbbb",
      "resource_id": "res_cccccccccccccccccccccccccc",
      "value": "1.0",
      "issued_at": "2026-04-24T10:15:00.000000Z",
      "valid_for_seconds": 60,
      "expires_at": "2026-04-24T10:16:00.000000Z",
      "sequence_number": 12,
      "metadata": {}
    }
  ],
  "poll_after_seconds": 10
}

200 OK

Empty queue.

JSON
{
  "code": 2000,
  "desired_states": [],
  "poll_after_seconds": 10
}

Desired-state item fields

desired_id

Stable Realer id for this desired-state request. Echo it in command feed data when reporting the outcome.

device_id

Public control-device id.

command_id

Public command id to apply.

resource_id

Public resource id for the resource-control context.

value

Desired command value to apply locally.

issued_at

Server time when Realer issued the desired state.

valid_for_seconds

Relative validity window from issue time.

expires_at

Server expiry time. Report stale instead of applying after this time.

sequence_number

Monotonic number for this command/resource desired-state stream.

metadata

Structured diagnostic metadata.

Reading a desired state marks it as delivered for diagnostics. The acknowledgement still happens through feed data: send command_id, the physical value, a stable message_id, and the returned desired_id. Use report_status only for rejected or stale outcomes.

Local physical command reports use the feed-data endpoint without desired_id. They update reported/current state but do not remove a pending desired-state item from this queue and do not mark it applied, rejected, stale, failed, or superseded.

Request and response example

Feed-data requests use JSON over HTTPS and return application-level results in the response body.

cURL
curl "https://api.therealer.com/iot/v1/devices/cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa/feed-data" \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer b11db7f6c816568eb3b156df3aeaa5' \
  -d '{"sensor_id":"sen_bbbbbbbbbbbbbbbbbbbbbbbbbb","value":"21.4","value_received_at":"2026-04-24T10:15:00Z","message_id":"sensor-senbbbb-20260424T101500Z"}'
POST /iot/v1/devices/{device_id}/feed-data

Send one command acknowledgement or one sensor reading over HTTPS.

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

Path parameters
device_id
(required)

Public id of the authenticated control device.

Type: String

Example: cdv_aaaaaaaaaaaaaaaaaaaaaaaaaa

Request body
JSON
{
  "sensor_id": "sen_bbbbbbbbbbbbbbbbbbbbbbbbbb",
  "value": "21.4",
  "value_received_at": "2026-04-24T10:15:00Z",
  "message_id": "sensor-senbbbb-20260424T101500Z"
}
Fields
command_id

Exactly one of command_id or sensor_id is required.

sensor_id

Use for sensor readings instead of command_id.

value

Raw source value to validate through the canonical semantic contract.

value_received_at

Canonical source event time for the submitted value.

message_id

Transport-level idempotency key scoped to the authenticated device and semantic source.

desired_id

Optional Realer desired-state id when this command report answers a command sent by Realer.

report_status

Optional command outcome for reports with desired_id: applied, rejected, or stale. For local physical command reports, omit this field; if sent without desired_id, only reported is valid.

Responses

201 Created

JSON
{
  "code": 2000,
  "status": "created",
  "message_id": "sensor-snsbbbb-20260424T101500Z",
  "warnings": []
}

422 Unprocessable Content

Validation failure

JSON
{
  "code": 4022,
  "error": {
    "type": "validation_failed",
    "message": "feed data ingestion failed",
    "details": {
      "sensor_id": ["is invalid for this device"]
    }
  },
  "request_id": "..."
}
Do not treat HTTP success alone as the application result. Check the JSON code field and, when present, error.type for stable device behavior.

Runtime error handling

HTTP or application result Firmware handling
2xx HTTP with code 2000 Accept the application result and continue the local loop.
2xx HTTP with feed-data status idempotent Treat the replay as success for the same message_id.
4xx HTTP with stable numeric code Use the response-code contract for local behavior. Do not retry blindly unless the documented error is retryable for the device.
401 or expired token Request a new OAuth token, then retry only the current safe operation with the same idempotency key when applicable.
5xx, timeout, or network failure Keep or enter local safe behavior, then retry with backoff and stable message_id values for the same physical event.

Command and sensor handling

Desired-state items represent command requests from Realer. Sensor catalog entries describe values the device can report.

  • For a desired command that is applied locally, send command feed data with the observed physical value.
  • For a desired command that cannot be applied or is stale, send command feed data with report_status set to rejected or stale.
  • For a local physical switch, button, or actuator change, send command feed data without desired_id.
  • For sensor readings, send sensor feed data with the current value, the measurement time, and a unique message_id.
  • After reconnecting, send the latest physical command state and latest relevant sensor state so Realer can reconcile the UI.

When to use MQTT instead

Use the MQTT flow when firmware keeps a broker connection open, needs command desired-state messages without polling, publishes feed data with MQTT over TLS, or needs asynchronous application acknowledgements on a subscribed topic.