# MQTT

> Connect Thalovant SDKs or a custom MQTT client over TLS with per-client broker credentials, scoped topics, and encrypted frames.

Source: https://docs.thalovant.com/developers/mqtt/

---
Use MQTT when a broker is a better fit than a direct WSS or HTTPS connection.

MQTT is still a secure public protocol. Clients connect to the broker with TLS, authenticate with per-client credentials, and publish only to the topics allowed for that client.

## When To Use MQTT

  
    Devices or small services need a broker-mediated path.
  
  
    The client can reach a broker more reliably than a direct hub route.
  
  
    Many clients need isolated topic access through one public broker endpoint.
  
  
    Use WSS first unless MQTT solves a real deployment problem.
  

## What Must Exist

1. **MQTT is enabled on the hub.** WSS can stay the default while MQTT is optional.
2. **The broker is reachable over TLS.** Public clients should use `mqtts://`.
3. **The client identity includes MQTT credentials.** Create or download a fresh identity, or open Connection details, after enabling MQTT.
4. **Topic access is scoped to the client.** Publish and subscribe only under the `topic_prefix` the identity returns.

## Credential Shape

When MQTT is enabled, the client identity carries an `mqtt` block:

```json
{
  "mqtt": {
    "endpoint": "mqtts://mqtt.thalovant.com:8883",
    "username": "client-access-key",
    "password": "client-broker-password",
    "topic_prefix": "<namespace>/<hub-id>/<client-id>",
    "tls": true
  }
}
```

| Field | Meaning |
| --- | --- |
| `endpoint` | Broker URL. Public clients should use `mqtts://`. |
| `username` | Broker username for this client. It is the client access key. |
| `password` | Broker password for this client. |
| `topic_prefix` | Full topic base for this client. Append a channel suffix instead of hardcoding a path. |
| `tls` | Whether the client should connect over TLS. |

  The broker password, hub password, and client static private key are secrets. Keep them in protected storage.

## Topics

The identity's `topic_prefix` is the full base for every topic this client uses. It looks like `<namespace>/<hub-id>/<client-id>`, where `<client-id>` is the client access key. Treat it as opaque: read `topic_prefix` from the identity and append the channel suffix instead of assembling the path yourself.

| Channel | Topic | Purpose |
| --- | --- | --- |
| Inbound | `<topic_prefix>/in` | The client publishes requests here. |
| Outbound | `<topic_prefix>/out` | The client subscribes here for replies. |
| Status | `<topic_prefix>/status` | Retained presence and status for this client. |

  These channels are the protocol's convention. What a client may actually publish or subscribe to is governed by the hub's per-connection broker ACL. A subscribe can be refused — for example on `<topic_prefix>/out` or `<topic_prefix>/status` — until that connection's ACL grants it. If replies never arrive after a successful connect, the connection's ACL likely needs the subscribe grant.

## Get MQTT Credentials

A client's MQTT credentials come from its identity. To read them for a custom client:

1. **Open the connection.** In [Connections](/concepts/clients/), select the client, then open its **Connection details** panel.
2. **Verify it is you.** Confirm with your authenticator app or a recovery code. The panel is gated because it returns secrets.
3. **Copy the broker credentials.** Take the endpoint (`mqtts://mqtt.thalovant.com:8883`), username (the access key), broker password, and topic prefix. Keep the complete identity as well: its hub password is what authenticates the session with the hub. You can also download `_identity.json`.

If two-step sign-in blocks the panel, set it up first on [Profile and Security](/account/profile-security/).

  An existing connection may need to open Connection details once to pick up MQTT credentials after MQTT is enabled.

## Custom MQTT Clients

You can connect any MQTT client to the broker with the credentials above, without Thalovant Voice or a managed runtime. Reaching the broker is the easy part.

The `<topic_prefix>/in` and `<topic_prefix>/out` topics carry the sign-in and authentication messages, followed by encrypted application messages. Subscribe to the outbound topic before sending anything. Encrypted messages are raw binary, not base64 or JSON; keep every chunk of one message in order and never replay a message.

A custom client must sign in to the broker and authenticate with the hub before sending application requests. Use the MQTT transport in a compatible [Python, Node.js, Go, or Rust SDK](/developers/sdks/#supported-versions), or drive the protocol with the embedded C library and your own transport. A broker connection alone is not an authenticated hub session.

Keep the SDK's saved keys across reconnects. After the broker connection is lost, authenticate again before sending. Use different identities for client processes connected at the same time.

  Always connect with `mqtts://` and validate the broker certificate. TLS protects the broker credentials and the sign-in. The hub session is a second layer: it encrypts the application messages and proves the hub is the hub. Neither one replaces the other.

## Python

```python
from thalovant import ThalovantClient, ThalovantControlPlane

api = ThalovantControlPlane()
api.login("you@example.com", "password")

result = api.create_client_identity(
    "hub-id",
    name="python-mqtt-client",
    preferred_protocols=("mqtt", "wss"),
)

identity = result.identity
if identity.mqtt is None:
    raise RuntimeError("MQTT is not available for this identity.")

with ThalovantClient(identity, protocol="mqtt") as client:
    print(client.ask("Reply over MQTT.").text)
```

## Node.js

```ts

const api = new ThalovantControlPlane();
await api.login("you@example.com", "password");

const result = await api.createClientIdentity("hub-id", {
  name: "node-mqtt-client",
  preferredProtocols: ["mqtt", "wss"],
});

if (!result.identity.mqtt) {
  throw new Error("MQTT is not available for this identity.");
}

const client = new ThalovantClient(result.identity, { protocol: "mqtt" });
try {
  const reply = await client.ask("Reply over MQTT.");
  console.log(reply.text);
} finally {
  await client.close();
}
```

## Go

```go
result, err := control.CreateClientIdentityForHubID(ctx, "hub-id", thalovant.BootstrapIdentityOptions{
	Name: "go-mqtt-client",
	PreferredProtocols: []thalovant.HubProtocol{
		thalovant.ProtocolMQTT,
		thalovant.ProtocolWSS,
	},
})
if err != nil {
	log.Fatal(err)
}
if result.Identity.MQTT == nil {
	log.Fatal("MQTT is not available for this identity.")
}

client, err := thalovant.NewClientWithOptions(result.Identity, thalovant.ClientOptions{
	Protocol: thalovant.ProtocolMQTT,
})
if err != nil {
	log.Fatal(err)
}
defer client.Close(ctx)
```

## Rust

```rust
let result = control
    .create_client_identity_for_hub_id(
        "hub-id",
        BootstrapIdentityOptions {
            name: "rust-mqtt-client".into(),
            preferred_protocols: vec![HubProtocol::Mqtt, HubProtocol::Wss],
            ..Default::default()
        },
    )
    .await?;

if result.identity.mqtt.is_none() {
    panic!("MQTT is not available for this identity.");
}

let client = Client::with_protocol(result.identity.clone(), HubProtocol::Mqtt)?;
```

## Common Issues

| Symptom | Check |
| --- | --- |
| The SDK says MQTT is unsupported | Use Python, server-side Node.js, Go, or Rust. Kotlin, Swift, .NET, and the Node browser build do not own an MQTT transport. Also confirm the hub and identity enable MQTT. |
| The identity has no `mqtt` block | Enable MQTT on the hub, then open Connection details or download a fresh identity to pick up broker credentials. |
| Broker auth fails | Confirm the username is the access key and the client uses the latest broker password. Rotate the identity if needed. |
| TLS fails | Use the `mqtts://` endpoint and confirm the broker certificate is valid. |
| A publish succeeds but nothing replies | Confirm the outbound subscription and the hub authentication completed before sending. A broker acknowledgement alone does not confirm hub authentication or an application reply. |
| A reconnect fails authentication | Keep the SDK's saved keys, check the hub password, and authenticate again before sending. |
| Messages are ignored | Publish to `<topic_prefix>/in` and subscribe to `<topic_prefix>/out`. Do not invent topic paths. |

  If WSS works and the client does not need a broker, keep using WSS.
