# Developer Docs

> Build a voice skill, or connect your app to a Thalovant hub with an SDK.

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

---
There are two things you can build here, and they are different jobs.

A **skill** runs on the hub and answers a particular kind of request — the code
behind "what's the weather?". An **SDK** is a library your own app uses to send
requests to a hub and read the answers.

  
  

## Your First Request :badge[New]{variant=tip}

Pick the tab for your language. Every SDK does the same four things: sign in,
create a client identity on a hub, connect over WSS, ask. The reply's text is
what the hub would have said out loud.

  
    ```python
    from thalovant import ThalovantClient, ThalovantControlPlane

    api = ThalovantControlPlane()

    hub = api.list_public_hubs(limit=1)["data"][0]
    print(hub["title"], hub["slug"])

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

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

    with ThalovantClient(result.identity, protocol="wss") as client:
        reply = client.ask("Tell me a short clean joke.")
        print(reply.text)
    ```
  
  
    ```ts
    import { ThalovantClient, ThalovantControlPlane } from "@thalovant/sdk";

    const api = new ThalovantControlPlane();

    const publicHubs = await api.listPublicHubs({ limit: 1 });
    const [hub] = publicHubs.data as Array<{ id: string; slug: string; title: string }>;
    console.log(hub.title, hub.slug);

    await api.login("you@example.com", "password");

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

    const client = new ThalovantClient(result.identity, { protocol: "wss" });
    try {
      const reply = await client.ask("Tell me a short clean joke.");
      console.log(reply.text);
    } finally {
      await client.close();
    }
    ```
  
  
    ```go
    package main

    import (
    	"context"
    	"fmt"
    	"log"

    	thalovant "github.com/thalovant/thalovant-go-sdk"
    )

    func main() {
    	ctx := context.Background()
    	control := thalovant.NewDefaultControlPlane("")

    	_, err := control.Login(ctx, "you@example.com", "password", "")
    	if err != nil {
    		log.Fatal(err)
    	}

    	result, err := control.CreateClientIdentityForHubID(ctx, "hub-id", thalovant.BootstrapIdentityOptions{
    		Name: "go-demo-client",
    		PreferredProtocols: []thalovant.HubProtocol{
    			thalovant.ProtocolWSS,
    			thalovant.ProtocolHTTPS,
    			thalovant.ProtocolMQTT,
    		},
    	})
    	if err != nil {
    		log.Fatal(err)
    	}

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

    	reply, err := client.Ask(ctx, "Tell me a short clean joke.", thalovant.RequestOptions{})
    	if err != nil {
    		log.Fatal(err)
    	}

    	fmt.Println(reply.Text)
    }
    ```
  
  
    ```rust
    use thalovant::{
        BootstrapIdentityOptions, Client, ControlPlane, HubProtocol, RequestOptions,
    };

    #[tokio::main]
    async fn main() -> thalovant::Result<()> {
        let mut control = ControlPlane::default();

        control.login("you@example.com", "password", None).await?;

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

        let client = Client::with_protocol(result.identity.clone(), HubProtocol::Wss)?;
        let reply = client
            .ask("Tell me a short clean joke.", RequestOptions::default())
            .await;
        let closed = client.close().await;
        let reply = reply?;
        closed?;
        println!("{}", reply.text);

        Ok(())
    }
    ```
  
  
    ```kotlin
    import com.thalovant.sdk.CreateClientIdentityOptions
    import com.thalovant.sdk.ThalovantClient
    import com.thalovant.sdk.ThalovantControlPlane
    import kotlinx.coroutines.runBlocking

    fun main() = runBlocking {
        val api = ThalovantControlPlane()

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

        val result = api.createClientIdentity(
            "hub-id",
            CreateClientIdentityOptions(name = "kotlin-demo-client"),
        )

        val client = ThalovantClient(result.identity)
        try {
            val reply = client.ask("Tell me a short clean joke.")
            println(reply.text)
        } finally {
            client.close()
        }
    }
    ```
  
  
    ```swift
    import ThalovantSDK

    let api = ThalovantControlPlane()

    try await api.login(email: "you@example.com", password: "password")

    let result = try await api.createClientIdentity(
        hubId: "hub-id",
        options: CreateClientIdentityOptions(name: "swift-demo-client")
    )

    let client = try ThalovantClient(identity: result.identity)
    do {
        try await client.connect()
        let reply = try await client.ask("Tell me a short clean joke.")
        print(reply.text)
        await client.close()
    } catch {
        await client.close()
        throw error
    }
    ```
  
  
    ```csharp
    using Thalovant;

    var api = new ThalovantControlPlane();

    await api.LoginAsync("you@example.com", "password");

    var result = await api.CreateClientIdentityAsync(
        "hub-id",
        new CreateClientIdentityOptions("dotnet-demo-client"));

    using var client = new ThalovantClient(result.Identity);
    await client.ConnectAsync();
    var reply = await client.AskAsync("Tell me a short clean joke.");
    Console.WriteLine(reply.Text);
    await client.CloseAsync();
    ```
  

Each SDK page explains the pieces: where the identity is kept, which protocols
the hub accepts, what to pass as context, and how to listen for events.

## Building A Skill

Start with the [Pocket Quiz tutorial](/developers/skillkit-tutorial/). You can
write and test the whole thing on your own computer before you've a hub at
all. It explains what each file is for and tells you what you should see after
every step.

When its checks pass, [Add Your Own Skill](/manage/add-your-own-skill/) puts it
on a test hub. Then ask it a question out loud, try a follow-up, and check that
"stop" ends the game.

[Writing a Skill](/developers/writing-a-skill/) is the reference you'll reach
for afterwards — helpers, conversation rules, tests, packaging.
[Keep Your Skill Responsive](/developers/skillkit-runtime/) covers caching,
bounded service calls, and background refresh.

## Connecting Your App

Thalovant creates the client identity and its access rule; your SDK uses that
identity to connect straight to the hub.

1. **Choose a hub.** A public hub is the easiest thing to test against.
2. **Create a client identity.** This needs API access on the workspace.
3. **Install an SDK.** Pick the language your app is already written in.
4. **Connect over WSS.** It's the default, and every SDK supports it.
5. **Add HTTPS or MQTT only if you need them.** Python, Node.js, Go, and Rust
   support both once the hub enables them. Kotlin, Swift, and .NET are WSS-only;
   embedded C uses whatever transport your firmware already has.
6. **Ask for an SDK origin if you're in a browser.** A customer-owned domain
   needs review before cross-origin calls are accepted.
7. **Add context last.** User, device, session, and trace metadata are worth
   adding once the first plain request works.

  
  
  
  
  
  
  
  

## About The REST API

Everything here goes through an SDK, and that's on purpose. The SDKs are the
supported surface: they handle the encrypted, authenticated session, the saved
keys a reconnect needs, request correlation, and the retry and cancellation
behaviour that a hand-rolled HTTP client has to get right on its own.

There's no published REST reference, so if you're looking for one, this is
why rather than an omission. Pick the SDK closest to your language — even
[embedded C](/developers/sdks/embedded-c/), which brings its own transport and
leaves the sockets to you.

## Connecting An Agent

The [MCP server](/developers/mcp/) exposes hubs to Claude, Codex, Cursor, and
other agents as tools, so an agent can ask a hub something or manage skills
without you writing a client.

## Everything Else
