> For the complete documentation index, see [llms.txt](https://docs.nlx.ai/platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nlx.ai/platform/developers/conversation-sdk/examples/voice+-tm.md).

# Voice+™

The basic setup for Voice+ looks like this:

{% tabs %}
{% tab title="HTML" %}

<pre class="language-javascript" data-overflow="wrap" data-full-width="true"><code class="lang-javascript">&#x3C;html lang="en">
  &#x3C;head>
    &#x3C;title>Touchpoint Sample HTML&#x3C;/title>
    &#x3C;meta name="viewport" content="width=device-width, initial-scale=1">
  &#x3C;/head>
  &#x3C;body>
    &#x3C;script type="module">
      import { create } from "https://unpkg.com/@nlxai/touchpoint-ui/lib/index.js?module";
      
      const touchpoint = await create({
        config: {
          applicationUrl: "REPLACE_WITH_APPLICATION_URL",
          headers: {
            "nlx-api-key": "REPLACE_WITH_API_KEY"
          },
          languageCode: "en-US",
        },
<strong>        input: "voiceMini",
</strong><strong>        bidirectional: {}
</strong>      });
    &#x3C;/script>
  &#x3C;/body>
&#x3C;/html>
</code></pre>

{% endtab %}

{% tab title="Typescript + NPM" %}

<pre class="language-typescript" data-overflow="wrap"><code class="lang-typescript">import { create } from "@nlxai/touchpoint-ui";
      
const touchpoint = await create({
  config: {
    applicationUrl: "REPLACE_WITH_APPLICATION_URL",
    headers: {
      "nlx-api-key": "REPLACE_WITH_API_KEY"
    },
    languageCode: "en-US",
  },
<strong>  input: "voiceMini",
</strong><strong>  bidirectional: {}
</strong>});
</code></pre>

{% endtab %}
{% endtabs %}

Note the `voiceMini` input type (this will make the Touchpoint user interface small and relatively unobtrusive allowing the user to keep interacting with the interface of your site). The `bidirectional` flag opens a command channel allowing the NLX voice application to affect your website.

### Automatic context

By default Voice+ will operate in fully automatic mode:

* it will automatically gather context about the available form fields and hyperlinks on the current page using accessibility APIs that will be made available to the LLM
* it will handle user requests for navigation and form filling automatically.

You may want to customise one or both of these processes (it often is the case that the automatic behavior covers 90% of what you need and a simple customisation can make the whole experience perfect).

Let's look at an example:

{% tabs %}
{% tab title="HTML" %}

<pre class="language-javascript" data-overflow="wrap" data-full-width="true"><code class="lang-javascript">&#x3C;html lang="en">
  &#x3C;head>
    &#x3C;title>Touchpoint Sample HTML&#x3C;/title>
    &#x3C;meta name="viewport" content="width=device-width, initial-scale=1">
  &#x3C;/head>
  &#x3C;body>
    &#x3C;script type="module">
      import { create } from "https://unpkg.com/@nlxai/touchpoint-ui/lib/index.js?module";
      
      const touchpoint = await create({
        config: {
          applicationUrl: "REPLACE_WITH_APPLICATION_URL",
          headers: {
            "nlx-api-key": "REPLACE_WITH_API_KEY"
          },
          languageCode: "en-US",
        },
        input: "voiceMini",
<strong>         bidirectional: {
</strong><strong>          customizeAutomaticContext({ context, state }) {
</strong><strong>            const linksAlwaysAvailable = {
</strong><strong>              "Product": "/index.asp?pageId=prod",
</strong><strong>              "Terms &#x26; Conditions": "/index.asp?cat=legal&#x26;p0",
</strong><strong>            };
</strong><strong>            return {
</strong><strong>              context: {
</strong><strong>                ...context,
</strong><strong>                destinations: [
</strong><strong>                  ...context.destinations,
</strong><strong>                  Object.keys(linksAlwaysAvailable),
</strong><strong>                ],
</strong><strong>              },
</strong><strong>              state: {
</strong><strong>                ...state,
</strong><strong>                links: {
</strong><strong>                  ...state.links,
</strong><strong>                  ...linksAlwaysAvailable,
</strong><strong>                },
</strong><strong>              },
</strong><strong>            };
</strong><strong>          },
</strong><strong>          navigation(action, destination, availableDestinations) {
</strong><strong>            if (action === "page_custom" &#x26;&#x26; destination != null) {
</strong><strong>              const url = availableDestinations[destination];
</strong><strong>              if (url != null) {
</strong><strong>                router.navigate(url);
</strong><strong>              }
</strong><strong>            } else if (action === "page_next") {
</strong><strong>              history.forward();
</strong><strong>            } else if (action === "page_previous") {
</strong><strong>              history.back();
</strong><strong>            }
</strong><strong>          },
</strong><strong>        }
</strong>      });
    &#x3C;/script>
  &#x3C;/body>
&#x3C;/html>
</code></pre>

{% endtab %}

{% tab title="Typescript + NPM" %}

<pre class="language-typescript" data-overflow="wrap"><code class="lang-typescript">import { create } from "@nlxai/touchpoint-ui";

const touchpoint = await create({
  config: {
    applicationUrl: "REPLACE_WITH_APPLICATION_URL",
    headers: {
      "nlx-api-key": "REPLACE_WITH_API_KEY"
    },
    languageCode: "en-US",
  },
  input: "voiceMini",
<strong>  bidirectional: {
</strong><strong>    customizeAutomaticContext({ context, state }) {
</strong><strong>      const linksAlwaysAvailable = {
</strong><strong>        Product: "/index.asp?pageId=prod",
</strong><strong>        "Terms &#x26; Conditions": "/index.asp?cat=legal&#x26;p0",
</strong><strong>      };
</strong><strong>      return {
</strong><strong>        context: {
</strong><strong>          ...context,
</strong><strong>          destinations: [
</strong><strong>            ...context.destinations,
</strong><strong>            Object.keys(linksAlwaysAvailable),
</strong><strong>          ],
</strong><strong>        },
</strong><strong>        state: {
</strong><strong>          ...state,
</strong><strong>          links: {
</strong><strong>            ...state.links,
</strong><strong>            ...linksAlwaysAvailable,
</strong><strong>          },
</strong><strong>        },
</strong><strong>      };
</strong><strong>    },
</strong><strong>    navigation(action, destination, availableDestinations) {
</strong><strong>      if (action === "page_custom" &#x26;&#x26; destination != null) {
</strong><strong>        const url = availableDestinations[destination];
</strong><strong>        if (url != null) {
</strong><strong>          router.navigate(url);
</strong><strong>        }
</strong><strong>      } else if (action === "page_next") {
</strong><strong>        history.forward();
</strong><strong>      } else if (action === "page_previous") {
</strong><strong>        history.back();
</strong><strong>      }
</strong><strong>    },
</strong><strong>  },
</strong>});

</code></pre>

{% endtab %}
{% endtabs %}

In this example we are augmenting the context sent to the LLM with a few hardcoded links. We are also making sure the user navigation hook plays nicely with our client side routing library (in this example we are using Tanstack Router).

We can also make custom actions available to the voice assistant. The set of available custom actions can be dynamically modified during the user session, so the following API can be called repeatedly.

<pre class="language-typescript" data-overflow="wrap"><code class="lang-typescript">import { create } from "@nlxai/touchpoint-ui";
import * as z from "zod/v4";
import { foodOrder } from "../myApi/foodOrder";
      
const touchpoint = await create({
  config: {
    applicationUrl: "REPLACE_WITH_APPLICATION_URL",
    headers: {
      "nlx-api-key": "REPLACE_WITH_API_KEY"
    },
    languageCode: "en-US",
  },
  input: "voiceMini",
  bidirectional: {}
});

const burgerSchema = z.object({
  type: z
    .enum(["cheeseburger", "veggieburger", "hamburger"])
    .describe("Which variety of burger to order"),
  count: z
    .number()
    .min(1)
    .max(200)
    .default(1)
    .describe("How many burgers to order"),
});

// potentially later
<strong>touchpoint.setCustomBidirectionalCommands([
</strong><strong>  {
</strong><strong>    action: "burger",
</strong><strong>    description: "Order a hamburger",
</strong><strong>    schema: z.toJSONSchema(burgerSchema),
</strong><strong>    // Input allows us to pass additional context about the availability of the action
</strong><strong>    input: {
</strong><strong>      allergies: ["peanuts"],
</strong><strong>      dietaryPreference: "vegetarian"
</strong><strong>    },
</strong><strong>    handler: ({type, count} : z.infer&#x3C;typeof burgerSchema>) => {
</strong><strong>      foodOrder({
</strong><strong>        product: type,
</strong><strong>        quantity: count,
</strong><strong>      })
</strong><strong>    },
</strong><strong>  },
</strong><strong>]);
</strong></code></pre>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nlx.ai/platform/developers/conversation-sdk/examples/voice+-tm.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
