Skip to main content

DCM Device Config Discovery

When a DCM device starts up, it needs to know which product slots it has, what their properties are, and any device-type-specific configuration values. This information is delivered to the device over MQTT - firmware does not call any HTTP endpoint directly.

This page explains the full config discovery contract: MQTT topics, request payload, response payload, extensible attribute storage, and what to check when config responses do not arrive.

Who reads and writes config​

Config access is split between two audiences with different protocols:

AudienceAccess methodWhat they can do
Management clients (admin UI, integration tools)REST APICreate, update, and delete slots and device configuration
Device firmwareMQTT config/request → config/set flowRead-only: request the authoritative config the cloud holds for this device

Firmware never writes config back. If a device needs to report its currently applied config, it publishes on config/report. If the cloud needs to audit what a device has applied, it sends config/get.

MQTT config topics​

All config topics follow the standard DCM topic convention:

dcm/{tenant_id}/{device_id}/config/{message_type}
TopicDirectionPurpose
dcm/{tenant_id}/{device_id}/config/requestDevice → CloudDevice requests authoritative config from the cloud
dcm/{tenant_id}/{device_id}/config/setCloud → DeviceCloud delivers authoritative config to the device
dcm/{tenant_id}/{device_id}/config/getCloud → DeviceCloud asks the device to report its current applied config
dcm/{tenant_id}/{device_id}/config/reportDevice → CloudDevice reports its current applied config

config/request and config/set form the firmware startup pair: the device asks and the cloud answers synchronously.

config/get and config/report form the cloud audit pair: the cloud asks and the device answers. These are used by operators and tools to inspect what config a device currently has loaded, without any database lookup.

config/request — device asks for authoritative config​

The device publishes on config/request to ask the cloud for the latest configuration. The optional sections array controls which parts of the config are included in the response.

Request payload​

{
"correlation_id": "d3f1a2b4-0000-0000-0000-000000000001",
"sections": [
"device",
"slots",
"categories",
"attributes",
"attribute_values"
]
}
FieldTypeRequiredDescription
correlation_idUUIDNoUnique identifier echoed back in the config/set response if provided
sectionsarray of stringsNoWhich parts of the config to include. Missing or empty means all sections

The backend identifies the device from the MQTT topic path — device_id is not needed in the payload.

sections values​

SectionWhat the response includes
deviceDevice record fields: device key, device name, device type, status, and IoT endpoint details
slotsSlot list for the device with slot identifiers, item catalog, quantity, capacity, and slot status
categoriesCategory definitions assigned to this device type
attributesAttribute definitions for those categories
attribute_valuesActual attribute values stored for this device

A device that only needs to refresh its slot list can send "sections": ["slots"]. A device that needs a full re-initialisation sends all five sections, or omits sections entirely.

config/set — cloud delivers authoritative config​

The cloud publishes config/set in response to a config/request. The payload mirrors the sections the device requested and includes only the data for those sections.

Response payload​

{
"correlation_id": "d3f1a2b4-0000-0000-0000-000000000001",
"tenant_id": "t1000000-0000-0000-0000-000000000001",
"device_id": "a1b2c3d4-0000-0000-0000-000000000001",
"requested_sections": [
"device",
"slots",
"categories",
"attributes",
"attribute_values"
],
"generated_at": "2026-06-19T09:00:00.123456+00:00",
"device": {
"device_id": "a1b2c3d4-0000-0000-0000-000000000001",
"device_key": "SHOWER-001",
"device_name": "Poolside Shower Unit 1",
"device_type_code": "SHOWER",
"device_status_code": "ONLINE",
"iot_thing_name": "dcm-t1000000-SHOW-001",
"iot_endpoint_name": "abc123.iot.ap-southeast-2.amazonaws.com",
"firmware_version": null,
"metadata": null
},
"slots": [
{
"slot_id": "b2c3d4e5-0000-0000-0000-000000000001",
"slot_number": 1,
"slot_name": "Shower Head 1",
"slot_status_code": "AVAILABLE",
"item_id": null,
"quantity": "0",
"slot_uom": null,
"capacity": null,
"slot_state": null,
"metadata": null,
"items": [
{
"item_id": "f6a7b8c9-0000-0000-0000-000000000001",
"item_key": "SHW-SVC-5MIN",
"item_name": "5-Minute Shower",
"display_order": 10
},
{
"item_id": "f6a7b8c9-0000-0000-0000-000000000002",
"item_key": "SHW-SVC-10MIN",
"item_name": "10-Minute Shower",
"display_order": 20
},
{
"item_id": "f6a7b8c9-0000-0000-0000-000000000003",
"item_key": "SHW-SVC-15MIN",
"item_name": "15-Minute Shower",
"display_order": 30
}
]
}
],
"categories": [
{
"category_id": "e4f5a6b7-0000-0000-0000-000000000001",
"category_code": "SHOWER_CONFIG",
"category_name": "Shower Configuration",
"category_description": null,
"parent_category_id": null,
"entity_type": "DCM_DEVICE",
"applies_to_type": "SHOWER",
"display_order": 10,
"metadata": null
}
],
"attributes": [
{
"attr_id": "f5a6b7c8-0000-0000-0000-000000000001",
"category_id": "e4f5a6b7-0000-0000-0000-000000000001",
"attr_code": "HEARTBEAT_INTERVAL",
"attr_name": "Heartbeat Interval (seconds)",
"data_type": "NUMBER",
"lookup_code": null,
"default_value": "60",
"is_required": true,
"is_searchable": false,
"is_filterable": false,
"validation_rule": null,
"uom_code": null,
"display_order": 10,
"display_group": null,
"metadata": null
}
],
"assignments": [
{
"assignment_id": "a1b2c3d4-0000-0000-0000-0000000000a1",
"entity_type": "DCM_DEVICE",
"entity_id": "a1b2c3d4-0000-0000-0000-000000000001",
"category_id": "e4f5a6b7-0000-0000-0000-000000000001",
"is_primary": true,
"metadata": null
}
],
"attribute_values": [
{
"value_id": "v1a2b3c4-0000-0000-0000-000000000001",
"entity_type": "DCM_DEVICE",
"entity_id": "a1b2c3d4-0000-0000-0000-000000000001",
"attr_id": "f5a6b7c8-0000-0000-0000-000000000001",
"value_text": null,
"value_number": 30.0,
"value_date": null,
"value_boolean": null,
"uom_code": null,
"metadata": null
}
]
}

Top-level envelope fields​

FieldDescription
correlation_idEchoed from the request, if provided
tenant_idUUID of the owning tenant
device_idUUID of the device
requested_sectionsSorted list of sections included in this response
generated_atISO 8601 timestamp of when the payload was built

Device payload fields​

FieldDescription
device_idUUID of this device
device_keyHuman-readable identifier (e.g. SHOWER-001)
device_nameFriendly display name
device_type_codeDevice type lookup code (e.g. SHOWER, VENDING_MACHINE)
device_status_codeCurrent operational status
iot_thing_nameAWS IoT Thing name used as the MQTT client ID
iot_endpoint_nameAWS IoT Core endpoint hostname for this device
firmware_versionFirmware version string, if recorded
metadataArbitrary JSON metadata

Firmware can compare device.iot_thing_name in this payload against the thing name it was configured with to verify they match.

Slot payload fields​

FieldDescription
slot_idUUID of the slot record
slot_numberPrimary slot identifier. Integer position on the device (1, 2, 3, …)
slot_nameFriendly name for the slot (e.g. Shower Head 1)
slot_status_codeCurrent availability status (AVAILABLE, IN_USE, RESERVED, EMPTY, DISABLED)
item_idThe item for the currently active session — null when the slot is idle
quantityCurrent stock level (decimal string)
slot_uomUnit of measure for quantity, if applicable
capacityMaximum stock level
slot_stateArbitrary JSON state blob maintained by the device
metadataArbitrary JSON metadata
itemsItem catalog — the list of items this slot is configured to sell. Each entry has item_id, item_key, item_name, and display_order

config/get and config/report — cloud audits device state​

The cloud uses this pair to inspect what config a device currently has applied. This is distinct from config/request/config/set, which delivers what the cloud thinks the device should have.

config/get (cloud → device):

{
"correlation_id": "e4a2b3c4-0000-0000-0000-000000000003",
"sections": ["slots", "attribute_values"]
}

The device responds on config/report with its currently applied values in the same sections format as config/set.

config/report (device → cloud):

{
"correlation_id": "e4a2b3c4-0000-0000-0000-000000000003",
"device_id": "a1b2c3d4-0000-0000-0000-000000000001",
"slots": [ ... ],
"attribute_values": [ ... ]
}

Extensible attribute storage​

Device-type configuration uses the Extensible Fields framework. The framework stores categories, attribute definitions, and attribute values separately, linked by device type and device instance. This lets each device type have its own set of configuration fields without requiring schema changes.

Category and attribute hierarchy​

Category (scoped to device type)
└── Attribute definitions
└── Category assignments (links device type → category)
└── Attribute values (links device instance → attribute → typed value)

Attributes use typed value columns. Only the column matching the attribute's data_type is populated; the rest are null.

Attribute data typeValue column
TEXTvalue_text
NUMBERvalue_number (serialized as a float)
BOOLEANvalue_boolean
LOOKUPvalue_text (stores the lookup value code)
DATEvalue_date (ISO 8601 string)

End-to-end device startup flow​

The following sequence describes a device connecting to the cloud for the first time and fetching its full configuration.

Key points in this flow:

  1. The device connects using its X.509 certificate and private key. Connectivity state is driven by AWS IoT presence, not by a message the device publishes.
  2. The device subscribes to config/set before publishing config/request, to avoid missing the response.
  3. The cloud receives the config/request, reads device, slot, and attribute data, and publishes the config/set response synchronously.
  4. The device applies the received config — slot assignments, attribute values, device name — then publishes a status report and starts its heartbeat.

Troubleshooting missing config responses​

If the device publishes config/request but never receives config/set, work through this checklist:

1. Confirm the device is subscribed to config/set before publishing config/request​

The device must subscribe to dcm/{tenant_id}/{device_id}/config/set before publishing the request. If the subscription is established after the response is published, the message is lost (QoS 0) or queued to the next session (QoS 1). Subscribe first.

2. Verify the device is registered and active in the web client​

Open Devices → All Devices and confirm the device is listed and its status is not PROVISIONING or DECOMMISSIONED. A device that has not completed provisioning cannot receive config.

3. Confirm slots and items are configured​

Open the device's Slots tab and confirm each slot has its item catalog set up. The config/set response includes an empty slot list if no slots are configured.

4. Confirm the device UUID matches​

The backend extracts tenant_id and device_id from the MQTT topic path. If the topic segment uses a device key or any identifier other than the device's UUID, the backend will not find the device record and will drop the message silently.

If none of these checks resolve the issue — the topic is correct, the IDs match, and the device and slots are configured — no error is sent back on the config topics. Re-check the IDs, then contact Raytio support.

Further reading​