MQTT Data Streaming¶
Subscribe to real-time device data over MQTT. This is the recommended way for external systems to receive device uplinks as they happen.
Overview¶
N-Hub exposes an MQTT bridge that streams device data to authenticated clients. Two authentication methods are supported:
- X.509 client certificates (recommended) — strongest security, no credential rotation needed until expiry
- Username/password — simpler setup, uses the API key's username and token as credentials
Both methods require TLS and scope data access to the API key's enterprise.
graph LR
A[Your System] <-->|MQTT over TLS| B[MQTT Bridge] <-->|Internal| C[Device Uplinks]
Quick Start (Certificate Auth)¶
1. Create an API Key¶
Create a service account (API key) in the N-Hub portal under Settings > API Keys, or via the API:
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"username": "svc-mqtt-prod", "is_service_account": true}' \
https://api.dev-au-03.nnnco.io/api/v4/manage/users/
Note the id (UUID) — this is your api_key_id.
2. Generate an MQTT Certificate¶
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Production MQTT Client",
"api_key_id": "<api-key-uuid>",
"validity_hours": 8760
}' \
https://api.dev-au-03.nnnco.io/api/v4/manage/mqtt-certificates/
The response contains three PEM fields — save all three immediately:
{
"id": "...",
"name": "Production MQTT Client",
"certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"private_key_pem": "<PEM-encoded EC private key>",
"ca_certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"fingerprint": "a1b2c3d4...",
"expires_at": "2027-03-17T00:00:00Z"
}
Save each PEM to a file:
# Save the three PEM files (the private key is only returned once)
echo "<certificate_pem>" > client.crt
echo "<private_key_pem>" > client.key
echo "<ca_certificate_pem>" > ca.crt
chmod 600 client.key
3. Connect with MQTT¶
mosquitto (CLI)¶
mosquitto_sub \
--cafile ca.crt \
--cert client.crt \
--key client.key \
-h mqtt.dev-au-03.nnnco.io -p 8883 \
-t "v4/groups/<enterprise_code>.master/+/uplinks" \
-v
Python (paho-mqtt)¶
import ssl
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc, properties=None):
print(f"Connected (rc={rc})")
client.subscribe("v4/groups/<enterprise_code>.master/+/uplinks")
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.tls_set(
ca_certs="ca.crt",
certfile="client.crt",
keyfile="client.key",
tls_version=ssl.PROTOCOL_TLS_CLIENT,
)
client.on_connect = on_connect
client.on_message = on_message
client.connect("mqtt.dev-au-03.nnnco.io", 8883)
client.loop_forever()
Node.js (mqtt.js)¶
const mqtt = require('mqtt');
const fs = require('fs');
const client = mqtt.connect('mqtts://mqtt.dev-au-03.nnnco.io:8883', {
ca: fs.readFileSync('ca.crt'),
cert: fs.readFileSync('client.crt'),
key: fs.readFileSync('client.key'),
});
client.on('connect', () => {
console.log('Connected');
client.subscribe('v4/groups/<enterprise_code>.master/+/uplinks');
});
client.on('message', (topic, message) => {
console.log(`${topic}: ${message.toString()}`);
});
Replace <enterprise_code> with your enterprise path (e.g., nnn.acme). Your administrator can provide this value.
Quick Start (Username/Password Auth)¶
Username/password authentication is a simpler alternative that doesn't require certificate management. Use the API key's username and token as MQTT credentials.
1. Create an API Key¶
Create a service account in the portal under Manage > API Keys. Note the username (e.g., api-sp-global-04f62b) and the API token shown once at creation.
2. Connect with MQTT¶
mosquitto (CLI)¶
mosquitto_sub \
-h mqtt.dev-au-03.nnnco.io -p 8883 \
-u "api-sp-global-04f62b" \
-P "YOUR_API_TOKEN" \
--cafile ca.crt \
-t "v4/groups/<enterprise_code>.master/+/uplinks" \
-v
CA certificate still required
TLS is always required. Use the platform's CA certificate (ca.crt) for server verification. You can obtain it by creating an MQTT certificate (the ca_certificate_pem field in the response), or from your administrator.
Python (paho-mqtt)¶
import ssl
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc, properties=None):
print(f"Connected (rc={rc})")
client.subscribe("v4/groups/<enterprise_code>.master/+/uplinks")
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.tls_set(
ca_certs="ca.crt",
tls_version=ssl.PROTOCOL_TLS_CLIENT,
)
client.username_pw_set("api-sp-global-04f62b", "YOUR_API_TOKEN")
client.on_connect = on_connect
client.on_message = on_message
client.connect("mqtt.dev-au-03.nnnco.io", 8883)
client.loop_forever()
Node.js (mqtt.js)¶
const mqtt = require('mqtt');
const fs = require('fs');
const client = mqtt.connect('mqtts://mqtt.dev-au-03.nnnco.io:8883', {
ca: fs.readFileSync('ca.crt'),
username: 'api-sp-global-04f62b',
password: 'YOUR_API_TOKEN',
});
client.on('connect', () => {
console.log('Connected');
client.subscribe('v4/groups/<enterprise_code>.master/+/uplinks');
});
client.on('message', (topic, message) => {
console.log(`${topic}: ${message.toString()}`);
});
Replace <enterprise_code> with your enterprise path (e.g., nnn.acme). Your administrator can provide this value.
Topics¶
MQTT topics follow the pattern:
| Topic Pattern | Description |
|---|---|
v4/groups/{enterprise}.master/+/uplinks |
All uplinks for your enterprise |
v4/groups/{enterprise}.master/{device_id}/uplinks |
Uplinks from a specific device |
v4/groups/{enterprise}.master/+/downlinks |
All downlink confirmations |
v4/groups/{enterprise}.master/+/joins |
All join events |
v4/groups/{enterprise}.master/# |
All message types for your enterprise |
Wildcards:
- # — matches all remaining levels (e.g., all devices and message types)
- + — matches a single level (e.g., any device ID)
Message Format¶
Each MQTT message contains a JSON payload:
{
"device_id": "ABC123",
"timestamp_ms": 1710300000000,
"source": "dl-mqtt",
"message_type": "uplink",
"device_type": "nsen-v2",
"readings": [
{"channel_id": "temperature", "value": 22.5, "unit": "\u00b0C"},
{"channel_id": "humidity", "value": 65, "unit": "%"}
],
"network_metadata": {
"rssi": -95,
"snr": 7.5,
"gateway_id": "GW001"
},
"location": {"latitude": -33.8688, "longitude": 151.2093},
"raw_payload": "0x1A2B3C..."
}
Certificate Management¶
List Certificates¶
curl -H "Authorization: Bearer <token>" \
https://api.dev-au-03.nnnco.io/api/v4/manage/mqtt-certificates/
Revoke a Certificate¶
curl -X POST -H "Authorization: Bearer <token>" \
https://api.dev-au-03.nnnco.io/api/v4/manage/mqtt-certificates/<cert-id>/revoke/
Revoked certificates are immediately rejected on the next connection attempt.
Certificate Limits¶
- 10 active certificates per API key
- Default validity: 1 year (8,760 hours)
- Maximum validity: 10 years (87,600 hours)
- The private key is returned once at creation — it is never stored on the server
Connection Details¶
| Setting | Value |
|---|---|
| Host | mqtt.dev-au-03.nnnco.io |
| Port | 8883 (TLS required) |
| Protocol | MQTT v3.1.1 or v5 |
| Auth | X.509 client certificate or username/password |
| QoS | 0 or 1 supported |
| Keep Alive | 60 seconds recommended |
Troubleshooting¶
| Issue | Solution |
|---|---|
SSL: CERTIFICATE_VERIFY_FAILED |
Ensure you're using the ca_certificate_pem from the create response, not a system CA |
Connection refused |
Verify the cert hasn't expired: openssl x509 -in client.crt -noout -dates |
Not authorized (cert auth) |
Check that the API key is active and belongs to your enterprise |
Not authorized (username/password) |
Verify the API token is correct — it is only shown once at API key creation |
| No messages received | Verify topic matches your enterprise code — check with your admin |
certificate revoked |
Generate a new certificate — the old one was revoked |