Skip to content
Console
Try one

    Finding and provisioning hubs

    List hubs, read one, create one, update it, and follow the operation that results.

    Part of SDK Functions. Every call below is shown in each language that has it.

    A hub has a slug, a name, and sometimes a catalog title. Showing the slug to a person is showing them plumbing, so every SDK ships one function that picks the best of the three and falls back to a readable form of the slug.

    from thalovant.hubs import hub_display_name
    hub_display_name({"slug": "kitchen-hub"}) # "Kitchen Hub"
    hub_display_name({"slug": "kitchen-hub", "name": "Kitchen"}) # "Kitchen"
    hub_display_name({}) # "A Thalovant hub"

    The order is catalog title, then name, then slug — and a name that is exactly the slug is treated as the slug, because it carries nothing the slug does not. With none of the three, the answer is A Thalovant hub rather than an empty label.

    Use public hub discovery before you ask a user to choose a hub.

    page = api.list_public_hubs(limit=12)
    for hub in page["data"]:
    print(hub["id"], hub["slug"], hub["title"])

    Use this when you have a hub reference from a URL, card, or saved choice.

    hub = api.get_public_hub("joke-garden")
    print(hub["title"], hub["public_ref"])

    Use this for hubs the signed-in workspace can see.

    page = api.list_hubs(limit=25)
    for hub in page["data"]:
    print(hub["id"], hub["title"])

    Use this after you already know the hub ID.

    hub = api.get_hub("hub-id")
    print(hub["title"])

    Use this when your code should create the hub. Discovery needs hubs:read and works on any plan. The four writes need hubs:write and a paid plan. For the etag rule, the immutable fields, and the error table, see Provision Hubs.

    skills = api.list_marketplace_skills()["data"]
    group = api.create_runtime_group({"name": "kiosks"})
    hub = api.create_hub({
    "name": "joke-garden",
    "runtime_group_id": group["id"],
    "spec": {"protocols": {"wss": {"enabled": True}}},
    })
    api.install_runtime_group_skill(group["id"], "skill-weather")
    api.release_runtime_group(group["id"], channel="stable")
    api.release_hub(hub["id"], channel="stable")

    Kotlin, Swift, and .NET expose the same calls with native names. Their SDK pages show the language-specific form.

    Hub update and delete need the hub’s current etag, sent as If-Match. Read the hub first: the etag is a body field, and the API sends no ETag response header.

    hub = api.get_hub("hub-id")
    hub = api.update_hub("hub-id", {"active": False}, etag=hub["etag"])
    api.delete_hub("hub-id", etag=hub["etag"])

    A missing or stale etag fails with HTTP 412 and changes nothing. Re-read the hub and retry.

    Use the operation ID returned by an asynchronous control-plane command. Stop at ready, failed, or timed_out; Git-only commands stop at committed.

    operation = api.get_operation("operation-id")
    print(operation.status)

    See Operations for lifecycle and retry guidance.

    Use these to manage the skill attachments of the runtime group selected by a hub. Every hub sharing the runtime group is affected. Each write returns an operation; wait polls it every 2 seconds for up to 120 seconds by default. The writes need hubs:write and a paid plan; listing needs hubs:inspect, which hubs:read includes. Errors are problem JSON with a root code, such as skill_version_already_installed on HTTP 409 or hub_without_runtime_group on HTTP 404, and the SDK error text reads HTTP <status>: <message> (<code>). They arrive in Python SDK 0.5.15 and Node SDK 0.3.15. For the routes, the app path, and the error table, see Add a Skill to a Hub.

    skills = api.list_hub_skills("hub-id") # HubSkillList; rows under .data
    print(skills.source, skills.observed_at)
    for skill in skills.data:
    print(skill.skill, skill.installed_version, skill.state, skill.update_available)
    api.install_hub_skill("hub-id", "skill-weather", version="latest", wait=True)
    api.update_hub_skill("hub-id", "skill-weather", version="1.2.0", wait=True)
    api.remove_hub_skill("hub-id", "skill-weather", wait=True)

    The list call returns the route’s envelope, with the rows under data and each row carrying installed_version, latest_version, update_available, and a state of pending, installed, failed, removing, drifted, quarantined, or unmanaged. Each write returns the accepted operation with operation_id, skill, version, previous_version, and state. Go, Rust, Kotlin, Swift and .NET also provide these calls and history in their native naming style; see their SDK pages for wait/resume options.