MQTT API Key Management
Overview
To receive real-time sensor data via MQTT, you need an integration and an API key linked to it. This guide shows how to create and manage these credentials programmatically using GraphQL mutations.
- A user account with Organization Manager role
- The GraphQL API endpoint:
https://haltian-iot-api.eu.haltian.io/v1/graphql - Familiarity with the Service API authentication flow
Workflow Overview
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#F6FAFA', 'primaryTextColor': '#143633', 'primaryBorderColor': '#143633', 'lineColor': '#143633', 'secondaryColor': '#C7FDE6', 'tertiaryColor': '#73F9C1', 'clusterBkg': '#ffffff', 'clusterBorder': '#143633', 'edgeLabelBackground': '#ffffff'}}}%%
flowchart TB
A["fa:fa-sign-in-alt Step 1: Authenticate"] --> B["fa:fa-plug Step 2: Create Integration"]
B --> C["fa:fa-key Step 3: Create API Key"]
C --> D["fa:fa-user-shield Step 4: Assign MqttIntegrator Role"]
D --> E["fa:fa-link Step 5: Link API Key to Integration"]
E --> F["fa:fa-broadcast-tower Ready to connect via MQTT"]
style A fill:#F6FAFA,stroke:#143633,stroke-width:2px,color:#143633
style B fill:#F6FAFA,stroke:#143633,stroke-width:2px,color:#143633
style C fill:#F6FAFA,stroke:#143633,stroke-width:2px,color:#143633
style D fill:#F6FAFA,stroke:#143633,stroke-width:2px,color:#143633
style E fill:#F6FAFA,stroke:#143633,stroke-width:2px,color:#143633
style F fill:#73F9C1,stroke:#143633,stroke-width:2px,color:#143633The setup involves five steps. Steps 2–5 only need to be done once per integration. After that, you only need to refresh the API key token before it expires.
Step 1: Authenticate
Log in with your user credentials to obtain a Bearer token for subsequent mutations.
mutation Login($email: String!, $organizationName: String!, $password: String!) {
login(loginInput: {
email: $email,
organizationName: $organizationName,
password: $password
}) {
accessToken
expiresIn
refreshExpiresIn
refreshToken
organizationId
}
}
{
"email": "your-email@example.com",
"organizationName": "your-organization",
"password": "your-password"
}
Use the returned accessToken in the Authorization header for all subsequent requests:
Authorization: Bearer eyJhbGciO...
See Service API Authentication for full details on token refresh and session management.
Step 2: Create MQTT Subscription Integration
Create an integration container. You typically need one integration per organization (or per logical data consumer).
mutation CreateMqttIntegration($description: String) {
createMqttSubscriptionIntegration(object: {
description: $description,
mqttSubscriptionState: "active"
}) {
id
description
mqttSubscriptionState
organizationId
createdAt
updatedAt
}
}
{
"description": "Production MQTT streaming"
}
Response
{
"data": {
"createMqttSubscriptionIntegration": {
"id": "31efcae6-39ff-45f2-b732-2090e2ed54c4",
"description": "Production MQTT streaming",
"mqttSubscriptionState": "active",
"organizationId": "2a749da0-5512-...",
"createdAt": "2026-03-24T10:00:00Z",
"updatedAt": "2026-03-24T10:00:00Z"
}
}
}
Save the id — this is your integration ID, needed in step 5 and in MQTT topic paths.
Step 3: Create API Key
Create an API key that will serve as MQTT credentials. The key name is for your reference (e.g., the customer or system it belongs to).
mutation CreateApiKey($name: String!) {
createApiKey(createApiKeyInput: {
name: $name
}) {
id
name
organizationId
createdAt
token {
token
issuedAt
expiresAt
}
}
}
{
"name": "Building Management System"
}
Response
{
"data": {
"createApiKey": {
"id": "c6f7b103-81df-4e73-8218-4f671056aee2",
"name": "Building Management System",
"organizationId": "2a749da0-5512-...",
"createdAt": "2026-03-24T10:01:00Z",
"token": {
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"issuedAt": "2026-03-24T10:01:00Z",
"expiresAt": "2026-06-22T10:01:00Z"
}
}
}
}
Save the token value immediately. The token is only returned at creation time and cannot be retrieved later. The token expires after 90 days from issuedAt.
The returned values map to MQTT credentials:
| API Key Field | MQTT Usage |
|---|---|
id | MQTT username and {api-key-id} in topic path |
token.token | MQTT password |
Step 4: Assign MqttIntegrator Role
Grant the API key permission to access MQTT streams by assigning the MqttIntegrator role.
mutation AssignApiKeyRole($apiKeyId: uuid!) {
assignApiKeyOrganizationRole(object: {
roleName: "MqttIntegrator",
apiKeyId: $apiKeyId
}) {
apiKeyId
roleName
organizationId
}
}
{
"apiKeyId": "c6f7b103-81df-4e73-8218-4f671056aee2"
}
The role name MqttIntegrator is case-sensitive and must be spelled exactly as shown.
Step 5: Link API Key to Integration
Connect the API key to the MQTT subscription integration created in step 2.
mutation AssignApiKeyToMqttIntegration($mqttSubscriptionId: uuid!, $apiKeyId: uuid!) {
assignMqttSubscriptionIntegrationApiKey(object: {
mqttSubscriptionId: $mqttSubscriptionId,
apiKeyId: $apiKeyId
}) {
mqttSubscriptionId
apiKeyId
}
}
{
"mqttSubscriptionId": "31efcae6-39ff-45f2-b732-2090e2ed54c4",
"apiKeyId": "c6f7b103-81df-4e73-8218-4f671056aee2"
}
After this step, the API key is fully configured. You can connect to the MQTT broker:
| Parameter | Value |
|---|---|
| Host | haltian-iot-mqtt.eu.haltian.io |
| Port | 18883 (TLS required) |
| Username | API Key ID (c6f7b103-81df-4e73-8218-4f671056aee2) |
| Password | API Key Token (eyJhbGciO...) |
| Topic | haltian-iot/events/{integration-id}/{api-key-id}/# |
See Stream API Topics for full topic structure and Stream API Examples for connection code.
Step 6: Refresh API Key Token
API key tokens expire after 90 days. Refresh before expiration to maintain uninterrupted MQTT access. The API key ID remains the same — only the token is regenerated.
mutation RefreshApiKey($apiKeyId: uuid!) {
refreshApiKeyToken(refreshApiKeyTokenInput: {
apikeyId: $apiKeyId
}) {
id
organizationId
token {
token
issuedAt
expiresAt
}
}
}
{
"apiKeyId": "c6f7b103-81df-4e73-8218-4f671056aee2"
}
Response
{
"data": {
"refreshApiKeyToken": {
"id": "c6f7b103-81df-4e73-8218-4f671056aee2",
"organizationId": "2a749da0-5512-...",
"token": {
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.new-token...",
"issuedAt": "2026-03-24T12:00:00Z",
"expiresAt": "2026-06-22T12:00:00Z"
}
}
}
}
After refreshing, update the MQTT client password with the new token. The previous token is immediately invalidated.
Listing and Deleting Keys
List All API Keys
Check existing keys and their expiration status:
query ListApiKeys {
apiKeys {
id
name
createdAt
updatedAt
organization {
id
name
}
token {
issuedAt
expiresAt
revokedAt
}
organizationRoles {
roleName
organizationId
}
}
}
List MQTT Integrations
View integrations and their attached API keys:
query ListMqttIntegrations {
mqttSubscriptionIntegration {
id
description
mqttSubscriptionState
organizationId
createdAt
updatedAt
apiKeys {
apiKeyId
}
}
}
Delete API Key
Permanently revoke an API key. The MQTT client using this key will be disconnected immediately.
mutation DeleteApiKey($apiKeyId: uuid!) {
deleteApiKey(deleteApiKeyInput: {
apikeyId: $apiKeyId
}) {
message
}
}
Complete Python Example
This script demonstrates the full setup workflow — from login to a working MQTT connection.
import requests
import json
import paho.mqtt.client as mqtt
import ssl
GRAPHQL_ENDPOINT = "https://haltian-iot-api.eu.haltian.io/v1/graphql"
MQTT_HOST = "haltian-iot-mqtt.eu.haltian.io"
MQTT_PORT = 18883
def graphql_request(query, variables=None, token=None):
"""Execute a GraphQL request."""
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
response = requests.post(
GRAPHQL_ENDPOINT,
json={"query": query, "variables": variables or {}},
headers=headers,
)
response.raise_for_status()
result = response.json()
if "errors" in result:
raise Exception(f"GraphQL error: {result['errors']}")
return result["data"]
# --- Step 1: Authenticate ---
login_data = graphql_request(
"""
mutation Login($email: String!, $organizationName: String!, $password: String!) {
login(loginInput: {email: $email, organizationName: $organizationName, password: $password}) {
accessToken
organizationId
}
}
""",
{
"email": "your-email@example.com",
"organizationName": "your-organization",
"password": "your-password",
},
)
token = login_data["login"]["accessToken"]
print(f"Authenticated. Organization: {login_data['login']['organizationId']}")
# --- Step 2: Create MQTT Integration ---
integration = graphql_request(
"""
mutation CreateMqttIntegration($description: String) {
createMqttSubscriptionIntegration(object: {
description: $description,
mqttSubscriptionState: "active"
}) { id description }
}
""",
{"description": "My MQTT Integration"},
token,
)
integration_id = integration["createMqttSubscriptionIntegration"]["id"]
print(f"Integration created: {integration_id}")
# --- Step 3: Create API Key ---
api_key = graphql_request(
"""
mutation CreateApiKey($name: String!) {
createApiKey(createApiKeyInput: { name: $name }) {
id
token { token issuedAt expiresAt }
}
}
""",
{"name": "My MQTT Key"},
token,
)
api_key_id = api_key["createApiKey"]["id"]
api_key_token = api_key["createApiKey"]["token"]["token"]
expires_at = api_key["createApiKey"]["token"]["expiresAt"]
print(f"API Key created: {api_key_id}")
print(f"Token expires: {expires_at}")
# IMPORTANT: Save api_key_token securely — it cannot be retrieved again
# --- Step 4: Assign MqttIntegrator Role ---
graphql_request(
"""
mutation AssignRole($apiKeyId: uuid!) {
assignApiKeyOrganizationRole(object: {
roleName: "MqttIntegrator", apiKeyId: $apiKeyId
}) { apiKeyId roleName }
}
""",
{"apiKeyId": api_key_id},
token,
)
print("MqttIntegrator role assigned")
# --- Step 5: Link API Key to Integration ---
graphql_request(
"""
mutation LinkKey($mqttSubscriptionId: uuid!, $apiKeyId: uuid!) {
assignMqttSubscriptionIntegrationApiKey(object: {
mqttSubscriptionId: $mqttSubscriptionId, apiKeyId: $apiKeyId
}) { mqttSubscriptionId apiKeyId }
}
""",
{"mqttSubscriptionId": integration_id, "apiKeyId": api_key_id},
token,
)
print("API Key linked to integration")
# --- Connect to MQTT ---
def on_connect(client, userdata, flags, rc):
if rc == 0:
topic = f"haltian-iot/events/{integration_id}/{api_key_id}/#"
client.subscribe(topic, qos=1)
print(f"Connected and subscribed to: {topic}")
else:
print(f"Connection failed: {rc}")
def on_message(client, userdata, msg):
payload = json.loads(msg.payload.decode())
print(f"{msg.topic}: {json.dumps(payload, indent=2)}")
client = mqtt.Client()
client.username_pw_set(api_key_id, api_key_token)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS)
client.on_connect = on_connect
client.on_message = on_message
print(f"Connecting to {MQTT_HOST}:{MQTT_PORT}...")
client.connect(MQTT_HOST, MQTT_PORT)
client.loop_forever()
Key Renewal Script
Standalone script for refreshing an expiring API key token:
import requests
GRAPHQL_ENDPOINT = "https://haltian-iot-api.eu.haltian.io/v1/graphql"
def graphql_request(query, variables=None, token=None):
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
response = requests.post(
GRAPHQL_ENDPOINT,
json={"query": query, "variables": variables or {}},
headers=headers,
)
response.raise_for_status()
result = response.json()
if "errors" in result:
raise Exception(f"GraphQL error: {result['errors']}")
return result["data"]
# Authenticate
login_data = graphql_request(
"""
mutation Login($email: String!, $organizationName: String!, $password: String!) {
login(loginInput: {email: $email, organizationName: $organizationName, password: $password}) {
accessToken
}
}
""",
{
"email": "your-email@example.com",
"organizationName": "your-organization",
"password": "your-password",
},
)
token = login_data["login"]["accessToken"]
# Refresh the API key token
API_KEY_ID = "c6f7b103-81df-4e73-8218-4f671056aee2" # your API key ID
result = graphql_request(
"""
mutation RefreshApiKey($apiKeyId: uuid!) {
refreshApiKeyToken(refreshApiKeyTokenInput: { apikeyId: $apiKeyId }) {
id
token { token issuedAt expiresAt }
}
}
""",
{"apiKeyId": API_KEY_ID},
token,
)
new_token = result["refreshApiKeyToken"]["token"]["token"]
expires_at = result["refreshApiKeyToken"]["token"]["expiresAt"]
print(f"Token refreshed. New expiration: {expires_at}")
# IMPORTANT: Update your MQTT client password with new_token
Related Pages
- Streaming & Integrations — Conceptual overview of the integration chain
- Stream API Topics — MQTT topic structure
- Stream API Examples — Connection code examples
- Service API Authentication — JWT token management
- Service API Mutations — Full mutation reference