> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognigy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Click To Call with a Selector Field

By the end of this guide, you will have learned how to set up a [Click To Call widget](/click-to-call/overview) that includes a selector field. The selector field will be displayed as an alternative to the voice input field to select predefined options. A selector field can be useful during voice conversations when the end user needs to select from a set of options, for example, when choosing a menu in a cantina setting.

## Prerequisites

* Configure an [AI Agent](/ai/agents/develop/manage-ai-agents).
* Configure a [speech provider](/ai/agents/test/voice-preview#set-up-a-speech-provider) in the [Voice Preview Settings](/ai/administer/access/project-settings#voice-preview-settings) section of the [Project settings](/ai/administer/access/project-settings).
* [Voice Gateway](/voice-gateway/getting-started#set-up-voice-gateway-for-your-organization) set up for your organization.
* Edit access to the HTML of the page where you will embed the widget.
* For testing purposes, allow microphone access in your browser and, if needed, in your device settings.

## Setup Overview

1. Configure a [Flow](/ai/agents/develop/projects-and-flows/overview) with an AI Agent Node and a Question Node. The Flow handles the conversation and opens the selector field on the Click To Call widget.
2. Configure a Voice Gateway Endpoint. The Voice Gateway Endpoint connects the Flow to Voice Gateway and activates the Click To Call widget.
3. Embed and test the widget. Add the widget script to your page and see the Click To Call widget working.

## Set Up Click To Call Widget with Selector Field

<Steps>
  <Step title="Create a Flow">
    1. Go to **Build > Flows** and click **+ New Flow**.
    2. On the **New Flow** panel, configure the following:

       * **Name** — add a unique name, for example, `Click To Call Assistant`.
       * (Optional) **Description** — add a relevant description, for example, `This Flow supports a Click To Call widget and a selector field`.
  </Step>

  <Step title="Configure an AI Agent Node">
    1. In the Flow editor, add an AI Agent Node and configure the following:

       * **AI Agent** — select the AI Agent you've previously created, for example, `Cantina Assistant`.
       * **Job Name** — `Cantina Specialist`. Save the Node.

    2. Next to the AI Agent Node, click **+** and select **Tool**.

    3. Configure the Tool Node as follows:

       * **Tool ID** — enter `select_menu`.
       * **Description** — enter `Trigger this tool as soon as the customer needs help selecting a menu`. Save the Node.
  </Step>

  <Step title="Configure a Say Node to Open the Selector">
    1. Below the `select_menu` child Node of the AI Agent Node, add a Say Node.
    2. Configure the Say Node as follows:

       * In the **Options** section, add the following code in the **Data** field:

       ```json theme={null}
       {
         "openInput": true
       }
       ```

       * In the **Settings** section, enter `Open selector` in the **Label** field. This label allows you to better identify the function of this Node.

       This Node triggers the event to open the selector field on the Click To Call widget.
    3. Add a Wait for Input Node to wait for the user to select or say an option.
  </Step>

  <Step title="Configure a Say Node to Close the Selector">
    1. Below the Wait for Input Node, add a Say Node and configure the following:

       * In the **Options** section, add the following code in the **Data** field:

       ```json theme={null}
       {
         "closeInput": true
       }
       ```

       * *(Optional)* In the **Settings** section, enter `Close selector` in the **Label** field.

       This Node closes the selector field on the widget after the user has answered.
  </Step>

  <Step title="Add a Resolve Tool Node to Return the Result to the AI Agent">
    Below the Wait for Input Node, add a Resolve Tool Node and configure the following:

    * **Answer Text** — enter `The user selected the following menu: {{ci.text}}`.

    This Node returns the user's menu to the AI Agent.
  </Step>
</Steps>

After completing these steps, your Flow should look like the following:

<Accordion title="Complete Flow">
  <Frame>
    <img class="image-center" src="https://mintcdn.com/cognigy-15abf2ba/o6JdRyZbiP_8bvBr/_assets/click-to-call/tutorials/selector-field.png?fit=max&auto=format&n=o6JdRyZbiP_8bvBr&q=85&s=5269cd191c96636870192a25b5f986c8" alt="Complete Flow with AI Agent, Say, Wait for Input, and Resolve Tool Nodes" style={{ width: '50%' }} width="930" height="1658" data-path="_assets/click-to-call/tutorials/selector-field.png" />
  </Frame>
</Accordion>

## Configure a Voice Gateway Endpoint

<Steps>
  <Step title="Create a Voice Gateway Endpoint">
    1. Go to **Deploy > Endpoints**.

    2. Click **+ New Endpoint**, select **Voice Gateway** and configure the following:

       * **Name** — for example, `Click To Call Cantina Support`.
       * **Flow** — Select the Flow you previously configured, in this example, `Click To Call Assistant`.

    3. Click **Save**.
  </Step>

  <Step title="Generate the Click To Call Embedding Code">
    1. In the Endpoint settings, find **Click To Call Embedding HTML**.
    2. Click the **Set Up Click To Call Integration** button in that field to generate the embedding code in the code editor. Hover over the code editor and click the **Copy to clipboard** button. You will use this code to embed the Click To Call widget in your website.
  </Step>

  <Step title="Enable and Configure the Click To Call Widget">
    1. In **Click To Call Widget Settings**:

       * **Enable Click To Call Widget** — activate the toggle.
       * **AI Agent Name** — Name shown on the widget, for example, `Cantina Support`.
       * **Tagline** — Short text under the name, for example, `Let me know what you want to eat!`.
       * *(Optional)* **AI Agent Avatar Logo URL** — Add a URL of the image shown on the widget, for example, `https://www.<domain>.com/logo.png`. If empty, the Cognigy.AI logo is used.
       * *(Optional)* **Theme** — select the color theme of the widget.

    2. Save the Endpoint.
  </Step>
</Steps>

## Embed and Test Widget

<Steps>
  <Step title="Embed the Widget in your Website">
    1. Paste the code you copied previously from the Endpoint settings into your page's `<body>` or before `</body>`. The code looks similar to the following:

    ```html expandable theme={null}
    <script src="https://github.com/Cognigy/click-to-call-widget/releases/latest/download/webRTCWidget.js"></script>
    <script>
        addEventListener("load", (event) => {
            if (window.initWebRTCWidget) {
                window.initWebRTCWidget(
                    "<ENDPOINT_URL>/<YOUR_ENDPOINT_CONFIG_TOKEN>"
                )
            }
        });
    </script>
    ```

    Where `<ENDPOINT_URL>` and `<YOUR_ENDPOINT_CONFIG_TOKEN>` are the URL and the Endpoint configuration token, respectively. For example, `https://endpoint-dev.cognigy.ai/ab8b929039b427fed7ee84bb799acd7f254fc9254be27c87a78fc8a70fb048ec` for the development environment or `https://endpoint.cognigy.ai/ab8b929039b427fed7ee84bb799acd7f254fc9254be27c87a78fc8a70fb048ec` for the production environment.

    2. Make sure the actual Endpoint configuration URL is the same as the one you copied from **Click To Call Embedding HTML** in the Voice Gateway Endpoint.
    3. Edit the widget script to add the dropdown menu and events to open and close the selector when the Flow sends `openInput: true` or `closeInput: true` in the Say Nodes. The following code is an example of how to do this:

    ```html expandable theme={null}
    <script src="https://github.com/Cognigy/click-to-call-widget/releases/latest/download/webRTCWidget.js"></script>
    <script>
        addEventListener("load", function () {
            if (typeof window.initWebRTCWidget !== "function") return;
            var token = "<ENDPOINT_URL>/<YOUR_ENDPOINT_CONFIG_TOKEN>";
            window.initWebRTCWidget(token).then(function (widget) {

                var currentSession = null;

                // Returns the widget container element (tries vc_, voice_connect_, and webrtc_ class names).
                function getWidgetContainer() {
                    return document.querySelector(".vc_widget_container") ||
                        document.querySelector(".voice_connect_widget_container") ||
                        document.querySelector(".webrtc_widget_container") ||
                        document.querySelector("[class*='widget_container']");
                }

                // Creates and shows a selector (dropdown) inside the transcript container or below the widget. Options: Mediterranean, Light, Comfort, Vegetarian. On Submit, sends the value via session.sendInfo() and hides the selector.
                function showSelectorField(session) {
                    currentSession = session;
                    var wrap = document.getElementById("widget-selector-wrap");
                    if (wrap) {
                        wrap.style.display = "block";
                        return;
                    }
                    var widgetEl = getWidgetContainer();
                    if (!widgetEl) widgetEl = document.body;
                    wrap = document.createElement("div");
                    wrap.id = "widget-selector-wrap";
                    wrap.style.cssText = "width:100%;max-width:460px;margin-top:8px;margin-bottom:8px;box-sizing:border-box;display:block;";
                    var select = document.createElement("select");
                    select.id = "widget-menu-select";
                    select.style.cssText = "width:100%;padding:8px 12px;font-size:14px;border:1px solid #ccc;border-radius:6px;box-sizing:border-box;margin-bottom:8px;";
                    select.setAttribute("aria-label", "Choose a menu");
                    var placeholder = document.createElement("option");
                    placeholder.value = "";
                    placeholder.textContent = "Select your menu";
                    select.appendChild(placeholder);
                    [{ value: "Mediterranean", label: "Mediterranean" }, { value: "Light", label: "Light" }, { value: "Comfort", label: "Comfort" }, { value: "Vegetarian", label: "Vegetarian" }].forEach(function (opt) {
                        var option = document.createElement("option");
                        option.value = opt.value;
                        option.textContent = opt.label;
                        select.appendChild(option);
                    });
                    var btn = document.createElement("button");
                    btn.type = "button";
                    btn.textContent = "Submit";
                    btn.style.cssText = "width:100%;padding:8px 12px;font-size:14px;border:1px solid #ccc;border-radius:6px;box-sizing:border-box;cursor:pointer;";
                    btn.setAttribute("aria-label", "Submit");
                    function submitSelection() {
                        var value = select.value ? select.value.trim() : "";
                        if (!value) return;
                        if (currentSession && typeof currentSession.sendInfo === "function") currentSession.sendInfo(value);
                        hideSelectorField();
                    }
                    select.addEventListener("change", function () { btn.disabled = !select.value; });
                    btn.disabled = true;
                    btn.addEventListener("click", submitSelection);
                    wrap.appendChild(select);
                    wrap.appendChild(btn);
                    if (widgetEl === document.body) {
                        widgetEl.appendChild(wrap);
                    } else {
                        var transcriptContainer = document.querySelector(".vc_transcript_container") ||
                            document.querySelector(".voice_connect_transcript_container") ||
                            document.querySelector(".webrtc_transcript_container") ||
                            document.querySelector("[class*='transcript_container']");
                        if (transcriptContainer) {
                            transcriptContainer.appendChild(wrap);
                        } else {
                            var stack = widgetEl.parentElement;
                            var transcriptWrapper = stack && (stack.querySelector(".vc_widget_transcript_wrapper") ||
                                stack.querySelector(".voice_connect_widget_transcript_wrapper") ||
                                stack.querySelector(".webrtc_widget_transcript_wrapper"));
                            if (transcriptWrapper) {
                                transcriptWrapper.insertAdjacentElement("afterend", wrap);
                            } else {
                                var contentContainer = widgetEl.querySelector(".vc_widget_content_container") ||
                                    widgetEl.querySelector(".voice_connect_widget_content_container") ||
                                    widgetEl.querySelector(".webrtc_widget_content_container");
                                if (contentContainer) {
                                    contentContainer.insertAdjacentElement("afterend", wrap);
                                } else {
                                    widgetEl.appendChild(wrap);
                                }
                            }
                        }
                    }
                }

                // Hides the selector and clears the current session reference.
                function hideSelectorField() {
                    currentSession = null;
                    var wrap = document.getElementById("widget-selector-wrap");
                    if (wrap) wrap.style.display = "none";
                }

                // Returns true if the INFO body contains openInput: true (Flow asks to show the selector).
                function hasOpenInput(body) {
                    if (body == null) return false;
                    if (typeof body === "string") {
                        try { body = JSON.parse(body); } catch (e) { return false; }
                    }
                    var infoBody = typeof body === "object" ? body : null;
                    if (!infoBody) return false;
                    if (infoBody.openInput === true) return true;
                    if (infoBody.data && infoBody.data.openInput === true) return true;
                    if (infoBody.data && infoBody.data.data && infoBody.data.data.openInput === true) return true;
                    return false;
                }

                // Returns true if the INFO body contains closeInput: true (Flow asks to close the selector).
                function hasCloseInput(body) {
                    if (body == null) return false;
                    if (typeof body === "string") {
                        try { body = JSON.parse(body); } catch (e) { return false; }
                    }
                    var infoBody = typeof body === "object" ? body : null;
                    if (!infoBody) return false;
                    if (infoBody.closeInput === true) return true;
                    if (infoBody.data && infoBody.data.closeInput === true) return true;
                    if (infoBody.data && infoBody.data.data && infoBody.data.data.closeInput === true) return true;
                    return false;
                }

                // Runs when a new call (RTC session) starts.
                widget.on("newRTCSession", function (session) {

                    // Runs when the Flow sends a SIP INFO: show the selector if openInput: true, hide it if closeInput: true.
                    session.on("newInfo", function (ev) {
                        if (ev.originator !== "remote") return;
                        var info = ev.info || ev;
                        var body = info && (info.body !== undefined ? info.body : info.request && info.request.body);
                        if (body === undefined && typeof ev.body !== "undefined") body = ev.body;
                        if (body == null) return;
                        try {
                            if (hasCloseInput(body)) { hideSelectorField(); return; }
                            if (hasOpenInput(body)) showSelectorField(session);
                        } catch (e) {}
                    });

                    // Runs when the call ends normally; hide the selector.
                    session.on("ended", hideSelectorField);

                    // Runs when the session is terminated; hide the selector.
                    session.on("terminated", hideSelectorField);
                });
            });
        });
    </script>
    ```
  </Step>

  <Step title="Test the Widget">
    1. Open the page in a browser, allow microphone access when prompted, and click <img src="https://mintcdn.com/cognigy-15abf2ba/3f0tglC4DBfZZ5qq/_assets/icons/start-voice-call.svg?fit=max&auto=format&n=3f0tglC4DBfZZ5qq&q=85&s=e4cde5644d6a7d8aed015feddf256d25" alt="start-voice-call" width="24" height="24" data-path="_assets/icons/start-voice-call.svg" /> on the widget to start a voice conversation with your AI Agent.
    2. After the AI Agent greets you, say you want to select a menu. The selector field opens on the widget.
    3. Try answering the question using the dropdown (Mediterranean, Light, Comfort, Vegetarian) and by voice.
  </Step>
</Steps>

## See Also

* [Click To Call](/click-to-call/overview)
* [Voice Gateway Endpoint](/ai/agents/deploy/endpoint-reference/voice-gateway)
* [Flows](/ai/agents/develop/projects-and-flows/overview)
* [AI Agent Node](/ai/agents/develop/node-reference/ai/ai-agent)
* [Manage AI Agents](/ai/agents/develop/manage-ai-agents)
