MQTT API Key Management

Create, renew, and manage MQTT streaming credentials programmatically via the GraphQL Service API

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.

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:#143633

The 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"
      }
    }
  }
}

The returned values map to MQTT credentials:

API Key FieldMQTT Usage
idMQTT username and {api-key-id} in topic path
token.tokenMQTT 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"
}

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:

ParameterValue
Hosthaltian-iot-mqtt.eu.haltian.io
Port18883 (TLS required)
UsernameAPI Key ID (c6f7b103-81df-4e73-8218-4f671056aee2)
PasswordAPI Key Token (eyJhbGciO...)
Topichaltian-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"
      }
    }
  }
}

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