Skip to content
Console
Try one

    What Can My Hub Be Asked?

    List every intent a hub answers, with the sentences a person says, from a connected client and without an API token.

    A connected client can ask its hub what it can be asked. The hub answers with every intent its skills registered, per language, and the sentences a person says to reach each one.

    The answer travels over the client’s own session. No API token, sign-in, or workspace role is involved. A voice client, a kiosk, an installer, or an agent can show a person what to say, or confirm that a new skill answers, with nothing more than its connection.

    An intent is one thing a skill can do, current.weather for instance. Ask a hub and it returns every intent it has, grouped by skill, with the sentences that reach each one, in each language. Any connected client can ask, as long as its connection may publish ovos.intent.list (and ovos.intent.describe, when the sentences have to be fetched separately). It costs nothing beyond the connection itself. A refusal is a policy error naming the message type, and it comes back at once rather than as a timeout; the fix is the connection’s message types in the dashboard.

    The inventory groups intents by skill. Each intent carries:

    FieldMeaning
    skill_idThe skill that registered the intent, such as thalovant-skill-weather.thalovant.
    nameThe intent name, such as current.weather. The full id is skill_id:name.
    engineHow the hub matches it. padatious matches whole sentences; adapt matches keyword sets.
    enabledWhether the hub answers to it right now.
    phrasesThe sentences that reach it, keyed by language.

    Sentences appear as the skill wrote them. A word in braces is a slot the person fills in: what is the weather in {location} matches “what is the weather in Montréal”. Keyword intents carry keyword sets rather than sentences, so they list without phrases.

    A weather skill that speaks English and French looks like this in the JSON form the SDKs and the thalovant intents --json command print:

    {
    "languages": ["en-us", "fr-fr"],
    "source": "intent-manifest",
    "denied": [],
    "skills": [
    {
    "skill_id": "thalovant-skill-weather.thalovant",
    "languages": ["en-us", "fr-fr"],
    "intents": [
    {
    "id": "thalovant-skill-weather.thalovant:current.weather",
    "skill_id": "thalovant-skill-weather.thalovant",
    "name": "current.weather",
    "engine": "padatious",
    "enabled": true,
    "phrases": {
    "en-us": ["what is the weather", "what is the weather in {location}", "how is it outside"],
    "fr-fr": ["quel temps fait-il", "quelle est la météo à {location}", "quelle est la météo"]
    }
    }
    ]
    }
    ]
    }

    source says how the inventory was read. intent-manifest carries sentences per language. engine-manifests is the names-only fallback described below, and denied names the query that triggered fallback.

    Ask for each language you want to show. Language tags compare without regard to case, and _ and - count as the same character: fr-fr, fr_FR, and fr-FR name one language. The hub answers with the tag it stores, so a request for fr-fr can come back labeled fr-FR.

    A skill lists only in the languages it ships. A skill with English sentences and no French ones appears in the English list and has no French phrases. The hub registers only the languages its runtime loads. If a whole language is missing, check the primary and secondary languages in Runtime.

    The hub answers two queries, and the connection needs only the ones its client sends:

    Message typeWhat it doesWhen the client sends it
    ovos.intent.listLists every registered intent for one language.Always.
    ovos.intent.describeReturns one intent’s registration, including its sentences.Only when the client has to ask for the sentences separately.

    A client that only wants to know which intents a hub registered needs ovos.intent.list alone: it gets the names, engines, and enabled state, and no sentences. Every SDK asks for the sentences unless you turn that off, and in Python the switch is client.intents(["en-us"], describe=False).

    Asking for the sentences doesn’t always mean sending ovos.intent.describe. The SDKs ask the hub to attach each intent’s definition to the listing itself, with include_definitions. A hub whose runtime honors that answers the listing with the sentences already in it, no describe is ever sent, and the connection never needs that second type. The SDK falls back to describing intent by intent only when the listing came back without definitions, and that’s when the connection needs ovos.intent.describe too.

    Which of the two happens depends on the runtime, and today no shipping runtime attaches them: OpenVoiceOS keeps its listing free of definitions by design, and Thalovant hubs run a runtime built from those same sources. A runtime that doesn’t recognize include_definitions ignores it and answers the plain listing, and the SDK then describes intent by intent. So in practice the connection needs both types. Allow both unless you’ve measured otherwise; that’s what Thalovant’s own connections do.

    Both are read-only. They reveal only intents the connection can already trigger, so allowing them adds no privilege. Connections that Thalovant creates for SDK clients and Thalovant Voice include both by default.

    To check or change them, open Connections in the dashboard, select the connection, and open its access settings. The list of message types the connection may publish (allowedTypes in the client spec) must contain each type the client sends. Add them when a connection was created before this feature shipped, or when its access was narrowed by hand, then reconnect the client.

    A connection that may not publish one of the types gets no reply. Instead the hub answers hive.policy.denied at once, naming the message type and the types the connection may publish:

    {
    "denied_type": "ovos.intent.list",
    "code": "acl_disallowed_type",
    "reason": "ovos.intent.list not in allowed_types",
    "data": {
    "msg_type": "ovos.intent.list",
    "allowed": ["recognizer_loop:utterance", "speak"]
    }
    }

    The SDKs turn that answer into a typed policy error the moment it arrives, rather than waiting for a timeout. The error names the refused type and carries code, reason, and the allowed list. In Python it’s ThalovantPolicyDeniedError, and its message tells you where to fix it:

    The hub refused 'ovos.intent.list': ovos.intent.list not in allowed_types.
    Allow this connection to publish 'ovos.intent.list' in the dashboard's connection settings.

    A refusal isn’t the only unhappy answer. The hub can also answer a query it accepted with ok: false and an error of its own, and the two queries don’t mean the same thing by it.

    When ovos.intent.list answers with an error, the listing failed. The hub hasn’t said what it registered, so the SDKs raise their runtime error carrying the hub’s error text. A listing that failed isn’t an empty hub, and reporting it as no intents would show a person a device that can do nothing. In Python it reads:

    ovos.intent.list failed: intent manifest unavailable

    Ask again once the hub is healthy. If it keeps failing, check the hub’s live state and its runtime on Hubs.

    When ovos.intent.describe answers with an error, that’s a normal, empty answer: the hub doesn’t know that registration. The intent stays in the inventory with its name, engine, and enabled state, and has no sentences. Nothing is raised, and the rest of the inventory is unaffected.

    When the hub refuses ovos.intent.list or doesn’t answer before its timeout, the SDKs fall back by default to two older queries, intent.service.adapt.manifest.get and intent.service.padatious.manifest.get. These return intent names only, in the form skill_id:intent_name, with no language and no sentences.

    A names-only result says so. source is engine-manifests, denied lists ovos.intent.list, and no intent carries phrases. It’s enough to confirm that a skill registered; it isn’t enough to show people what to say. When the fallback queries are refused as well, the policy error is raised.

    The current high-level SDKs all use this fallback for a silent listing. The legacy denied field names the unavailable query; it doesn’t prove a policy refusal. If the engine queries also time out, the operation raises the SDK timeout error. Replies and refusals carrying another request ID can’t complete this request.

    Turn the fallback off when your client should fail rather than degrade. In Python that’s client.intents(fallback=False).

    Fallback Handlers And Language Availability

    Section titled “Fallback Handlers And Language Availability”

    Intent phrases don’t describe every way a hub can answer. The SDKs can add an optional inventory probe using ovos.skills.fallback.list for fallback handlers and read ovos.skills.fallback.list.response. Allow that query on the connection when you want this information. The probe uses at most 1.5 seconds across connection, send, and reply wait; caller cancellation still stops the operation.

    fallbacks_known (or its language-native name) distinguishes a confirmed empty list from unknown support. Refusal, silence, malformed reply envelopes, and an explicit failed response leave support unknown. An accepted fallback array produces a known list of usable handlers; invalid rows are skipped. may_answer(lang) is conservative: it’s false only when fallback discovery succeeded with no handlers and no enabled intent has phrases for that language. Unknown support isn’t evidence that the hub can’t answer.

    Python, Node, Kotlin, Swift, and .NET add this information to their inventory. Go and Rust preserve their existing inventory type and expose an additive capability wrapper through IntentsWithCapabilities and intents_with_capabilities. Embedded C supplies fallback request frames and response classification; the caller implements the probe, result collection, and availability decision.

    Voice clients and kiosks

    Show a “what can I say” screen in the person’s language, built from the hub rather than typed by hand.

    Installers

    Confirm that a newly installed skill answers, in every language the runtime loads, before handing the hub over.

    Agents

    Let an agent read what the hub can do before it chooses a phrase, instead of guessing.

    The Python SDK 0.4.36 and newer reads the inventory with client.intents([...]), and the thalovant intents command prints it:

    Terminal window
    thalovant --identity _identity.json intents --lang en-us --lang fr-fr

    See List What the Hub Can Be Asked in the Python SDK guide. The Node.js, Go, Rust, Kotlin, Swift, and .NET SDKs have the same three calls under their own spelling, and the embedded C library builds and reads the two frames. Each SDK page names them and gives a working example.

    The current managed SDKs and MCP can render raw intent patterns as sentences. Enable the sentence option on the example helper to remove optional wording, choose alternatives, insert locale sample slots, and add capitalization and punctuation. Your explicit slot values override the samples. The original intent patterns remain available.

    Examples prefer complete phrases over prefixes and slot patterns. Rendered limits count unique nonempty results. Regional matching can use a nearby registered locale; it doesn’t translate a skill into a language it doesn’t support. Unknown locales retain bare text. See the SDK guides for versions and examples; Python requires its optional listing extra.

    SymptomCheck first
    A policy error names ovos.intent.list or ovos.intent.describeAdd that message type to the connection’s message types in the dashboard, then reconnect the client.
    Names but no sentences, and source is engine-manifestsThe connection may allow only fallback queries, or the listing received no reply. Check that the runtime implements the listing and allows ovos.intent.list; allow ovos.intent.describe when sentences are not attached.
    Listing failed with an error from the hubThe hub could not read its intent manifest. It is not an empty hub. Check its live state on Hubs, then ask again.
    One intent has no sentencesIt is a keyword intent, the hub does not know that registration, or its description did not arrive in time. Raise the timeout or describe that intent on its own.
    A whole language is missingCheck the runtime language configuration and fallback discovery. Missing phrases alone do not rule out an answer.
    A skill is missingCheck its install state and live status on Skills.
    The call times out with no policy errorCheck authenticated connection readiness, whether the runtime implements the query, and its response budget. See Connections.

    The inventory is ready to show people when:

    • every installed skill appears under the skill_id you expect;
    • each intent has sentences in every language the runtime loads;
    • source is intent-manifest and denied is empty;
    • the example sentences read like something a person would say.
    Read it with the Python SDK Review access rules