Skip to main content

MQTT API

This page is the reference for what the Embedded Stream Processor publishes and accepts over MQTT. If the device is a Face Matcher edge stream, you do not use these topics directly: the edge-stream-processor service consumes FrameData, Station manages configuration and licensing, and edge-streams-state-synchronizer fills the on-device watchlist; you consume results through Face Matcher's REST, GraphQL and RabbitMQ notifications. The topics below are for standalone integrations that run their own broker and consumer, and for debugging.

Two-way MQTT messaging between the device and a server application through a broker

Topic layout​

Every topic is prefixed with the root topic and client ID from the device's connection section (<topic>/<client_id>/..., for example edge-stream/lobby-cam-1/frame_data). The device speaks MQTT v3, which has no native request/response pattern, so responses to a request topic are published on the same topic with a /response suffix; where the topic allows it, append your own :request_id segment to tell concurrent requests apart (the response is published on .../<request_id>/response). Message payloads use the format set in messaging.format — Protobuf (default), JSON or YAML.

Topic (after <topic>/<client_id>/)DirectionKindPayload
frame_datadevice → youPeriodicFrameData
healthdevice → youRetainedHealthReport
settingsdevice → youRetainedcurrent settings
settings/updateyou → deviceRetainednew settings to apply at run time
logdevice → youPeriodic (every 5 s)log lines as text
userrequest / responseResponseRecords
user/<user_id>request / responseResponseRecord
user/<user_id>/face/add[/<request_id>]request / responseResponseimage or template in, Record out
user/<user_id>/metadata/<key>/<value>[/<request_id>]request / responseResponseempty in, empty out
user/<user_id>/delete[/<request_id>]request / responseResponseempty in, empty out
db/update[/<request_id>]request / responseResponseUpdateRequest in, UpdateResponse out
db/status[/<request_id>]request / responseResponseempty in, StatusResponse out

Message kinds: Retained messages are kept by the broker and delivered to new subscribers; Periodic messages are published on the device's schedule; Response messages answer a request and carry either the operation's result or an Error with a message string.

Health​

The retained health message is updated on every status change: Online right after connecting, Offline when the stream processor restarts (for example after a settings change) or shuts down cleanly, and OfflineDisconnected as the broker-delivered last will when the connection drops unexpectedly. The report also carries the device software version. Station's edge-stream health indicator is derived from this message.

FrameData​

frame_data is the primary output. The device publishes it whenever a new face is detected and at least every messaging.interval milliseconds while faces are tracked (and continuously when allow_empty_messages is on). Its content follows the device settings: crops only with crop.enable, templates only with face_extraction.enable, and so on. All coordinates are normalized to the frame (0.0–1.0).

MessageFields
FrameDatatimestamp, frame_number, frame_image (Image, data only with full_frame.enable), face_data[], lost_object_data[], client_id
FaceDataface_detection_data, face_tracking_data, landmarks_data[] (23 keypoints), face_mask_data, cropping_data, liveness_data[], template_data, identification_data[], identification (whether identification ran)
FaceDetectionDatabounding_box (x, y, width, height), detection_confidence (normalized), raw_detection_confidence
TrackingDatatracking_id, tracking_uuid, tracking_state (New, Tracked, Lost, Removed)
FaceLandmarkDatakeypoint_type (eye corners and centres, nose, mouth, eyebrows, face edges, chin), confidence, x, y
FaceMaskDataconfidence — mask present when above about 0.5
CroppingDatacrop_extension, crop_box, crop_image (Image)
LivenessDataliveness_type (PassiveDistant, PassiveNearby), score, raw_score, liveness_conditions[] (condition_type, value, thresholds, condition_met), conditions_met
TemplateDatatemplate (bytes, 522 B)
IdentificationDatauuid of the matched record, score (0.0–1.0), dsid (database state ID), meta_data[] key/value pairs
LostObjectDatatracking_uuid, first_time_appeared, last_time_appeared — emitted once when a tracklet is closed
Imagewidth, height, data (bytes), image_format (Raw, Jpeg, Png)

The authoritative Protobuf definitions (innovatrics.embedded.stream_processor.frame_data, common, health, db, user) ship with the device package; they are also reproduced in the Embedded Toolkit stream-processor messaging reference. Compile them with protoc for your language and subscribe to frame_data to decode messages.

A trimmed JSON FrameData (messaging.format: Json) with one identified face; landmarks, most keypoints and the image bytes are elided:

{
"timestamp": { "seconds": 1688110510, "nanos": 440625640 },
"frame_number": 200,
"frame_image": { "width": 1280, "height": 720, "data": null, "image_format": 0 },
"face_data": [
{
"face_detection_data": {
"bounding_box": { "x": 0.619, "y": 0.343, "width": 0.133, "height": 0.302 },
"detection_confidence": 0.247,
"raw_detection_confidence": 0.992
},
"face_tracking_data": {
"tracking_id": 3,
"tracking_uuid": "c8052a96-8c9d-4765-be94-1d6282cf4b32",
"tracking_state": 1
},
"landmarks_data": [
{ "keypoint_type": 0, "confidence": 0.66, "x": 0.645, "y": 0.457 },
{ "keypoint_type": 1, "confidence": 0.77, "x": 0.655, "y": 0.456 }
],
"face_mask_data": { "confidence": 0.02 },
"cropping_data": {
"crop_extension": 2,
"crop_box": { "x": 0.521, "y": 0.231, "width": 0.319, "height": 0.567 },
"crop_image": { "width": 250, "height": 250, "data": "...", "image_format": 1 }
},
"liveness_data": [
{ "liveness_type": 0, "score": 0.739, "raw_score": 0.256, "liveness_conditions": [], "conditions_met": true }
],
"template_data": { "template": "SUNGADFY/PwKA..." },
"identification_data": [
{ "uuid": "b8f21151-0c8e-4c6a-8f29-882b405ff9aa", "score": 0.416, "dsid": "GEN_ID_dbd738aa||2", "meta_data": [] }
],
"identification": true
}
],
"lost_object_data": [],
"client_id": "lobby-cam-1"
}

User topics​

The device keeps a local database of records — a UUID, one or more face templates and key/value metadata — in the file set by face_identification.storage, and matches every extracted template against it when face_identification.enable is true. The user topics manage single records. <user_id> is a UUID v4 that you choose; adding a face to an unknown UUID creates the record. Examples use the mqtt CLI client against a broker at 192.0.2.10 and a device with client ID lobby-cam-1.

Add a face — payload is a JPEG or PNG image, from which the device extracts a template with its own settings, or an already extracted template file. The response is the updated Record:

$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user/4af8b586-1361-4ff2-9a58-034aa9a03c0f/face/add -m:file face_image.jpg
$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user/4af8b586-1361-4ff2-9a58-034aa9a03c0f/face/add -m:file face_template.icf

Attach metadata — the key and value travel in the topic; the payload is empty. Metadata is returned in identification_data.meta_data whenever the record matches:

$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user/4af8b586-1361-4ff2-9a58-034aa9a03c0f/metadata/name/Jane -m:empty

List or get records — publish an empty message; the response is Records (all users, with templates and metadata) or one Record:

$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user -m:empty
$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user/4af8b586-1361-4ff2-9a58-034aa9a03c0f -m:empty

Delete a record:

$ mqtt pub -h 192.0.2.10 -t edge-stream/lobby-cam-1/user/4af8b586-1361-4ff2-9a58-034aa9a03c0f/delete -m:empty

Subscribe to the matching .../response topic before publishing to see the result, for example mqtt sub -h 192.0.2.10 -t 'edge-stream/lobby-cam-1/user/+/delete/response'.

DB topics​

The db topics apply batched changes and track the database state with a DSID (database state ID), a string you choose to label each state. This is the mechanism a server uses to keep a device in sync: it sends the changes that move the device from from_dsid to to_dsid, and the device answers with the DSID it now holds; if the update fails, the device reports failure_dsid instead, so the server knows to resend.

TopicRequestResponse
db/update[/<request_id>]UpdateRequest: from_dsid, to_dsid, failure_dsid, clear (wipe the database first), updates[] (MemberUpsert: member_info.uuid, templates[], meta_data[]), deletes[] (MemberDelete: member_info.uuid)UpdateResponse: current_dsid, optional error_message
db/status[/<request_id>]emptyStatusResponse: current_dsid, optional error_message

Requests are binary Protobuf (or JSON/YAML per messaging.format), so build them from the db.proto definitions rather than by hand. With Face Matcher, edge-streams-state-synchronizer drives these topics from the watchlists you select in Station; the dsid value that then appears in identification_data lets the server tell which watchlist state a match was made against. Templates you push must come from the same extraction algorithm the device uses, otherwise matching returns no candidates.

Settings and log topics​

The device publishes its effective configuration as a retained message on settings and applies a new configuration published to settings/update, which is how Station changes edge-stream processing without touching the device's file system. Sections that need a restart (solvers, license, log) restart the stream processor, which briefly reports Offline on health. The log topic carries the device's log lines every five seconds at the configured log.level, in the form <ISO timestamp> <LEVEL> <message>; subscribe to it when you cannot reach the device's own log file:

$ mqtt sub -h 192.0.2.10 -t edge-stream/lobby-cam-1/log
2025-12-16T11:22:32.966382Z DEBUG {client_id="lobby-cam-1"}:detection: Stream detection, detected 1 faces, 1 persons