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

# Configuration reference

> Every option you can pass to the AI Chat Widget — token, branding, identity, chat behavior, context, AI action request data, and custom mode components.

This page lists every option you can pass to the widget. For *how* to combine these options into a brand, launch flow, or visitor experience, start with [Configuration](/widget/configuration).

<Tip>
  Want to try options live and copy a working snippet? Open the **[Playground](/widget/playground)**.
</Tip>

<Info>
  Defaults shown as `undefined` mean the option has no default — the related feature stays off until you set the option. Defaults shown as language-specific (e.g. for [`textContent`](#textcontent)) come from the active [`language`](#language) translation, not a single hard-coded string.
</Info>

## Initialization

#### `token`

<ResponseField name="" type="string" required>
  Your organization's widget token. Find it in [Channels → AI Chat Widget](https://platform.open.cx/channels/configure/widget). The widget will not load without it.
</ResponseField>

## Branding

#### `assets`

<ResponseField name="" type="object" default={{}}>
  Brand visuals shown around the chat — organization logo and launcher icons.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="organizationLogo" type="string">
      Logo shown on the welcome screen.
    </ResponseField>

    <ResponseField name="widgetTrigger" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="openIcon" type="string">
          Icon shown on the launcher when the widget is closed.
        </ResponseField>

        <ResponseField name="closeIcon" type="string">
          Icon shown on the launcher when the widget is open.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

#### `bot`

<ResponseField name="" type="object">
  Identity of the AI shown in the widget header and AI message bubbles.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="name" type="string" default="AI Support Agent">
      Display name of the AI.
    </ResponseField>

    <ResponseField name="avatarUrl" type="string">
      Avatar image URL of the AI.
    </ResponseField>
  </Expandable>
</ResponseField>

#### `humanAgent`

<ResponseField name="" type="object">
  Default appearance applied to every human agent after <Tooltip tip="The AI transfers the conversation to a human agent with full context attached." cta="Learn more" href="/handoff/introduction">handoff</Tooltip>.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="avatarUrl" type="string">
      Default avatar image URL used for all human agents.
    </ResponseField>
  </Expandable>
</ResponseField>

#### `theme`

<ResponseField name="" type="object">
  Colors, panel sizing, spacing, and other layout-level visuals.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="palette" type="'neutral' | 'stone' | 'zinc' | 'slate'" default="stone" />

    <ResponseField name="primaryColor" type="string" default="hsl(0, 0%, 0%)" />

    <ResponseField name="widgetTrigger" type="object">
      Launcher button styling.

      <Expandable title="properties" defaultOpen>
        <ResponseField name="zIndex" type="number" default={10000000} />

        <ResponseField name="offset" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="bottom" type="number (pixels)" default={20} />

            <ResponseField name="right" type="number (pixels)">
              Defaults to `20` when the host page is `ltr`, otherwise `initial`.
            </ResponseField>

            <ResponseField name="left" type="number (pixels)">
              Defaults to `20` when the host page is `rtl`, otherwise `initial`.
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="size" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="button" type="number (pixels)" default={48} />

            <ResponseField name="icon" type="number (pixels)" default={24} />
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="widgetContentContainer" type="object">
      Outer container styling for the chat panel.

      <Expandable title="properties" defaultOpen>
        <ResponseField name="borderRadius" type="string" default="32px" />

        <ResponseField name="boxShadow" type="string" default="0 0px 100px 0px rgb(0 0 0 / 0.25)" />

        <ResponseField name="outline" type="string" default="none" />

        <ResponseField name="outlineColor" type="string" default="hsl(0 0% 50% / .5)" />

        <ResponseField name="transitionProperty" type="string" default="all" />

        <ResponseField name="transitionTimingFunction" type="string" default="cubic-bezier(0.16, 1, 0.3, 1)" />

        <ResponseField name="transitionDuration" type="string" default="800ms" />

        <ResponseField name="zIndex" type="number" default={10000001} />

        <ResponseField name="offset" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="side" type="number (pixels)" default={10} />

            <ResponseField name="align" type="number (pixels)" default={0} />
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="screens" type="object">
      Per-screen sizing for the welcome, sessions, and chat panels.

      <Expandable title="properties" defaultOpen>
        <ResponseField name="welcome" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="width" type="string" default="375px" />

            <ResponseField name="minHeight" type="string" default="200px" />
          </Expandable>
        </ResponseField>

        <ResponseField name="sessions" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="width" type="string" default="450px" />

            <ResponseField name="height" type="string" default="600px" />
          </Expandable>
        </ResponseField>

        <ResponseField name="chat" type="object">
          <Expandable title="properties" defaultOpen>
            <ResponseField name="width" type="string" default="525px" />

            <ResponseField name="height" type="string" default="700px" />

            <ResponseField name="withCanvas" type="object">
              Sizing applied while the canvas is open (used by [`modesComponents`](#modescomponents)).

              <Expandable title="properties" defaultOpen>
                <ResponseField name="width" type="string" default="min(1050px, 100vw)" />

                <ResponseField name="height" type="string" default="min(800px, 100vh)" />

                <ResponseField name="transitionTimingFunction" type="string" default="cubic-bezier(0.16, 1, 0.3, 1)" />

                <ResponseField name="transitionDuration" type="string" default="1000ms" />
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

#### `cssOverrides`

<ResponseField name="" type="string">
  Custom stylesheet that targets widget parts via `data-component` selectors. Use when the [`theme`](#theme) options are not enough.

  Inspect the DOM to find selectors like `data-component="chat/input_box/root"`. If a part you need to style does not yet have a selector, contact us and we will add one.

  <Expandable title="available selectors">
    ```ts theme={"dark"}
    export type OpenCxComponentNameU =
      | 'ui_lib/btn'
      | 'trigger/btn'

      /* Sessions screen */
      | 'sessions/root'
      | 'sessions/header'
      | 'sessions/new_conversation_btn'

      /* Chat screen */
      | 'chat/root'
      | 'chat/header'
      | 'chat/main/root'
      | 'chat/canvas/root'
      | 'chat/msgs/root'

      /* Agent message */
      | 'chat/agent_msg_group/root'
      | 'chat/agent_msg_group/avatar_and_msgs/root'
      | 'chat/agent_msg_group/avatar_and_msgs/avatar'
      | 'chat/agent_msg_group/avatar_and_msgs/msgs'
      | 'chat/agent_msg_group/root/avatar'
      | 'chat/agent_msg_group/suggestions'
      | 'chat/agent_msg/root'
      | 'chat/agent_msg/msg'

      /* User message */
      | 'chat/user_msg_group/root'
      | 'chat/user_msg_group/avatar/root'
      | 'chat/user_msg/root'
      | 'chat/user_msg/msg'

      /* Chat input */
      | 'chat/input_box/root'
      | 'chat/input_box/inner_root'
      | 'chat/input_box/textarea_and_attachments_container'
      | 'chat/input_box/textarea'
      | 'chat/input_box/attachments_container'

      /* Chat utilities */
      | 'chat/bot_loading/root'
      | 'chat/bot_loading/bouncing_dots_container'
      | 'chat/suggested_reply_btn'
      | 'chat/might_solve_user_issue_suggested_replies_container';
    ```
  </Expandable>

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      cssOverrides: `
        * {
          font-family: "Arial", sans-serif;
        }

        [data-component="ui_lib/btn"][data-variant="ghost"]:hover {
          background: rgba(68, 71, 70, 0.08);
        }

        [data-component="chat/agent_msg_group/root/avatar"] {
          border-width: 2px;
          border-color: white;
          display: initial;
        }
        [data-component="chat/agent_msg_group/avatar_and_msgs/avatar"] {
          display: none;
        }
        [data-component="chat/agent_msg_group/suggestions"] {
          padding-left: unset;
        }
      `,
    }
    ```
  </Expandable>
</ResponseField>

#### `language`

<ResponseField name="" type="string" default="en">
  Language code used for built-in widget copy.

  Supported codes:

  * `ar` Arabic
  * `da` Danish
  * `de` German
  * `en` English
  * `es` Spanish
  * `fi` Finnish
  * `fr` French
  * `it` Italian
  * `nl` Dutch
  * `no` Norwegian
  * `pl` Polish
  * `pt` Portuguese
  * `ro` Romanian
  * `sv` Swedish
  * `tr` Turkish

  <Info>
    Need another language? Contact support or open a [pull request](https://github.com/openchatai/widget).
  </Info>
</ResponseField>

#### `textContent`

<ResponseField name="" type="object">
  Override built-in copy on individual screens. Defaults come from the active [`language`](#language).

  <Expandable title="properties" defaultOpen>
    <ResponseField name="welcomeScreen" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="title" type="string" />

        <ResponseField name="description" type="string" />
      </Expandable>
    </ResponseField>

    <ResponseField name="sessionsScreen" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="headerTitle" type="string" />
      </Expandable>
    </ResponseField>

    <ResponseField name="chatScreen" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="headerTitle" type="string" />
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

#### `translationOverrides`

<ResponseField name="" type="{ [key in Language]?: Partial<TranslationInterface> }" default={{}}>
  Override individual translation strings per language. See [`language`](#language) for the list of supported codes.

  <Expandable title="TranslationInterface">
    ```ts theme={"dark"}
    export type TranslationInterface = {
      i_need_more_help: string;
      this_was_helpful: string;
      write_a_message_placeholder: string;
      your_issue_has_been_resolved: string;
      new_conversation: string;
      back_to_conversations: string;
      closed_conversations: string;
      no_conversations_yet: string;
      welcome_screen_title: string;
      welcome_screen_description: string;
      your_name_placeholder: string;
      your_email_placeholder: string;
      optional: string;
      start_chat_button: string;
      start_chat_button_loading: string;
    };
    ```
  </Expandable>

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      translationOverrides: {
        en: {
          i_need_more_help: `This didn't answer my question`,
          this_was_helpful: `It's all good!`,
        },
        ar: {
          start_chat_button: 'يلا ندردش',
        },
      },
    }
    ```
  </Expandable>
</ResponseField>

#### `timestamps`

<ResponseField name="" type="object" default={{}}>
  Display timestamps in the conversation.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="perMessageGroup" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="enabled" type="boolean" default={false}>
          Show a timestamp at the top of each message group.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

#### `disableTooltips`

<ResponseField name="" type="boolean" default={false}>
  Disable the in-widget hover tooltips on UI controls (e.g. header buttons). Has no effect on documentation tooltips.
</ResponseField>

#### `inline`

<ResponseField name="" type="boolean" default={false}>
  Embed the widget inside its parent element instead of floating over the page.

  When `true`, the widget content fills its parent container, the widget stays open, and the launcher button is hidden. Useful when you want a chat panel built into a page section.
</ResponseField>

#### `headerButtons`

<ResponseField name="" type="{ sessionsScreen?: Array<HeaderButton>; chatScreen?: Array<HeaderButton> }" default={{}}>
  Custom header buttons for the sessions and chat screens — expand-shrink the widget, close it, resolve the session, and so on.

  <Warning>
    Setting this option removes the default `close-widget` button on small screens. Add a `close-widget` entry yourself if you still need one.
  </Warning>

  <Expandable title="HeaderButton">
    ```ts theme={"dark"}
    type IconNameU =
      | 'Maximize'
      | 'Maximize2'
      | 'Minimize'
      | 'Minimize2'
      | 'Expand'
      | 'Shrink'
      | 'X'
      | 'SquareX'
      | 'CircleX'
      | 'Check'
      | 'CheckCheck'
      | 'CircleCheck'
      | 'CircleCheckBig'
      | 'SquareCheck'
      | 'SquareCheckBig';

    type HeaderButtonBase = {
      icon?: IconNameU;
      hideOnSmallScreen?: boolean;
      hideOnLargeScreen?: boolean;
    };

    type HeaderButton =
      | (HeaderButtonBase & {
          functionality: 'expand-shrink';
          /** Overridden by HeaderButtonBase.icon if set. */
          expandIcon?: IconNameU;
          /** Overridden by HeaderButtonBase.icon if set. */
          shrinkIcon?: IconNameU;
        })
      | (HeaderButtonBase & {
          functionality: 'close-widget';
          /**
           * Side effect run on click. Overrides the default close behavior —
           * useful when the widget's open/closed state is controlled externally.
           */
          handleClick?: () => void;
        })
      | (HeaderButtonBase & {
          functionality: 'resolve-session';
          /** What happens after the session is resolved. @default 'stay-in-chat' */
          onResolved?:
            | 'stay-in-chat'
            | 'reset-chat'
            | 'close-widget'
            | 'reset-chat-and-close-widget';
          /** Optional confirmation dialog before resolving. */
          confirmation?: {
            type: 'modal';
            title?: string;
            description?: string;
            confirmButtonText?: string;
            cancelButtonText?: string;
          };
          /** Behavior before the session is created. @default 'disabled' */
          behaviorBeforeSessionCreation?: 'disabled' | 'close-widget';
          /** Behavior after the session is resolved. @default 'disabled' */
          behaviorIfSessionIsResolved?:
            | 'disabled'
            | 'reset-chat'
            | 'close-widget'
            | 'reset-chat-and-close-widget';
        });
    ```
  </Expandable>

  <Expandable title="expand-shrink example">
    ```ts theme={"dark"}
    {
      headerButtons: {
        sessionsScreen: [
          {
            functionality: 'expand-shrink',
            expandIcon: 'Expand',
            shrinkIcon: 'Shrink',
          },
        ],
        chatScreen: [
          {
            functionality: 'expand-shrink',
            expandIcon: 'Expand',
            shrinkIcon: 'Shrink',
          },
        ],
      },
    }
    ```
  </Expandable>

  <Expandable title="resolve-session example">
    ```ts theme={"dark"}
    {
      headerButtons: {
        chatScreen: [
          {
            functionality: 'resolve-session',
            icon: 'X',
            behaviorBeforeSessionCreation: 'close-widget',
            behaviorIfSessionIsResolved: 'reset-chat-and-close-widget',
            confirmation: {
              type: 'modal',
              title: 'Close conversation',
              description: 'Are you sure you want to close this conversation?',
              confirmButtonText: 'Yes',
              cancelButtonText: 'No',
            },
          },
        ],
      },
    }
    ```
  </Expandable>

  <Expandable title="close-widget example">
    ```ts theme={"dark"}
    {
      headerButtons: {
        sessionsScreen: [
          {
            functionality: 'close-widget',
            icon: 'CircleX',
          },
        ],
        chatScreen: [
          {
            functionality: 'close-widget',
            icon: 'CircleX',
          },
        ],
      },
    }
    ```
  </Expandable>
</ResponseField>

## User

#### `collectUserData`

<ResponseField name="" type="boolean" default={false}>
  Show a built-in form on the welcome screen that asks for the visitor's name and email. The submitted values create a non-verified <Tooltip tip="A person on the customer side of the conversation. Verified contacts are authenticated server-to-server; non-verified contacts come from values typed in the widget or passed from your client.">contact</Tooltip>.

  Use [`prefillUserData`](#prefilluserdata) and [`extraDataCollectionFields`](#extradatacollectionfields) to tailor the form. For verified identity, see [Authentication](/widget/authentication).
</ResponseField>

#### `prefillUserData`

<ResponseField name="" type="object" default={{}}>
  Initial values for the `name` and `email` inputs in the welcome screen. Requires [`collectUserData`](#collectuserdata) to be `true`.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="name" type="string" />

    <ResponseField name="email" type="string" />
  </Expandable>
</ResponseField>

#### `extraDataCollectionFields`

<ResponseField name="" type="Array<string>" default={[]}>
  Extra free-text fields to add to the welcome form, beyond `name` and `email`. Requires [`collectUserData`](#collectuserdata) to be `true`.
</ResponseField>

#### `user`

<ResponseField name="" type="object" default={{}}>
  Verified or non-verified contact data passed from your app. If omitted, an anonymous contact is used.

  See [Authentication](/widget/authentication) for the full setup, including how to mint a verified token from your backend.

  <Expandable title="verified contact properties">
    <ResponseField name="token" type="string" required>
      Server-issued contact token. Generate it via the [authenticate-contact API](/api-reference/widget/authenticate-contact).
    </ResponseField>

    <ResponseField name="externalId" type="string">
      Optional ID that scopes the contact's sessions per workspace — use it when one email can belong to multiple accounts in your application.
    </ResponseField>
  </Expandable>

  <Expandable title="non-verified contact properties">
    <ResponseField name="data" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="name" type="string">
          Stored as `non_verified_name`. Will not override the name on a verified contact with the same email.
        </ResponseField>

        <ResponseField name="email" type="string" />

        <ResponseField name="avatarUrl" type="string" />

        <ResponseField name="customData" type="Record<string, string | number | boolean>">
          Arbitrary user attributes. Stored as `non_verified_custom_data`.

          <Expandable title="example">
            ```ts theme={"dark"}
            {
              org_id: 'org_185938572649',
              vip: true,
              loyalty_points: 1200,
            }
            ```
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="externalId" type="string">
      Optional ID that scopes the contact's sessions per workspace.
    </ResponseField>
  </Expandable>
</ResponseField>

## Chat

#### `initialMessages`

<ResponseField name="" type="Array<string>" default={['Hello, how can I help you?']}>
  Messages the visitor sees in a new chat session. They disappear after the visitor sends their first message.
</ResponseField>

#### `advancedInitialMessages`

<ResponseField name="" type="Array<{ message: string; persistent?: boolean }>">
  Same idea as [`initialMessages`](#initialmessages) with per-message persistence control. When set, this overrides `initialMessages`.

  When `persistent` is `true`, the message stays pinned at the top of the chat; otherwise it disappears after the first reply.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      advancedInitialMessages: [
        { message: 'Hello, how can I help you?', persistent: true },
        { message: 'Please select an option from the list below:', persistent: false },
      ],
    }
    ```
  </Expandable>
</ResponseField>

#### `initialQuestions`

<ResponseField name="" type="Array<string>" default={[]}>
  Suggested starting questions shown at the top of a new chat. Clicking one sends it as the visitor's first message.
</ResponseField>

#### `initialQuestionsPosition`

<ResponseField name="" type="'above-chat-input' | 'below-initial-messages'" default="above-chat-input">
  Where the suggested questions appear relative to the chat input.
</ResponseField>

#### `thisWasHelpfulOrNot`

<ResponseField name="" type="object">
  Prompt the visitor to confirm whether the latest AI reply solved their issue. Renders as two buttons: "This was helpful" and "I need more help".

  <Expandable title="properties" defaultOpen>
    <ResponseField name="enabled" type="boolean" default={true} />
  </Expandable>
</ResponseField>

## Behavior

#### `isOpen`

<ResponseField name="" type="boolean" default={false}>
  Whether the widget is open. Use to open the widget on first render (e.g. inside a webview) or to control its open state from your app.
</ResponseField>

#### `openAfterNSeconds`

<ResponseField name="" type="number">
  Open the widget automatically after N seconds on the page.
</ResponseField>

#### `oneOpenSessionAllowed`

<ResponseField name="" type="boolean" default={false}>
  When `true`, the **New conversation** button is hidden while an open session already exists. See [Conversation Sessions](/widget/conversation-sessions) for the user-facing effect.
</ResponseField>

#### `router`

<ResponseField name="" type="object">
  Override the default screen routing.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="goToChatIfNoSessions" type="boolean" default={true}>
      Navigate first-time visitors directly to the chat screen instead of the sessions list.
    </ResponseField>

    <ResponseField name="chatScreenOnly" type="boolean" default={true}>
      Show only the welcome and chat screens. The most recent open session is selected; if none exists, an empty conversation is opened and a session is created when the visitor sends a message. The "back to sessions" button is hidden.
    </ResponseField>
  </Expandable>
</ResponseField>

#### `anchorTarget`

<ResponseField name="" type="'_top' | '_blank'" default="_top">
  Target attribute applied to all links rendered in AI or human-agent replies.

  * `_top` — open links in the same tab.
  * `_blank` — open links in a new tab.
</ResponseField>

#### `disableSendingWhenAwaitingAIReply`

<ResponseField name="" type="boolean" default={true}>
  By default the send button is disabled while the AI is generating a reply. Set to `false` to let the visitor send another message before the AI finishes — the typing indicator stays visible; only the send button's disabled state is dropped.
</ResponseField>

## Context & custom data

#### `context`

<ResponseField name="" type="object" default={{}}>
  Runtime data sent with each visitor message so the AI can tailor its response. Good for page URL, current product, and other request-time signals.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      context: {
        page: { url: window.location.href },
        product_id: '99836009',
        product_name: 'Ineffective painkiller',
        product_price: 12.99,
      },
    }
    ```
  </Expandable>
</ResponseField>

#### `messageCustomData`

<ResponseField name="" type="object" default={{}}>
  Per-message metadata attached to each visitor message. Intended for human agents — the AI does not see it and it does not influence replies.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      messageCustomData: {
        platform: 'MOBILE',
        version: '1.3.57',
      },
    }
    ```
  </Expandable>
</ResponseField>

#### `sessionCustomData`

<ResponseField name="" type="Record<string, string | number | boolean>" default={{}}>
  Metadata attached to the session when it is created. Intended for human agents and for filtering sessions in the inbox — the AI does not see it.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      sessionCustomData: {
        visit_id: '0199e212-d7fd-71d3-808b-48d860bfa469',
      },
    }
    ```
  </Expandable>

  <Note>
    Keys prefixed with `opencx__` are reserved for OpenCX-specific controls. Set
    `opencx__ai_agent_version` to `'v2'` to upgrade that session to the latest AI
    agent. A session can only upgrade, never downgrade, so a lower value has no
    effect. The value is stored as session data, so you can also filter on it in the
    inbox. Omitting it uses the version from your channel settings.

    ```ts theme={"dark"}
    {
      sessionCustomData: {
        opencx__ai_agent_version: 'v2',
      },
    }
    ```
  </Note>
</ResponseField>

## AI action requests

These options attach data to every HTTP request the widget makes when the AI calls one of your APIs. For secure credentials, configure them in [General settings](https://platform.open.cx/settings) instead.

#### `headers`

<ResponseField name="" type="Record<string, string>" default={{}}>
  Custom headers added to every AI action request.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      headers: {
        auth_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      },
    }
    ```
  </Expandable>
</ResponseField>

#### `queryParams`

<ResponseField name="" type="Record<string, string>" default={{}}>
  Custom query parameters appended to every AI action request URL.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      queryParams: {
        session_id: '9de647ba-25b9-4d9e-b51a-7ab2f4c68d9d',
      },
    }
    ```
  </Expandable>
</ResponseField>

#### `bodyProperties`

<ResponseField name="" type="Record<string, string>" default={{}}>
  Custom properties merged into the body of every AI action request.

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      bodyProperties: {
        preferred_language: 'en',
      },
    }
    ```
  </Expandable>
</ResponseField>

## Components

#### `modesComponents`

<ResponseField name="" type="Array<ModeComponent>" default={[]}>
  Custom React components rendered in the canvas while a <Tooltip tip="A widget mode that takes over the chat area to drive a structured flow — onboarding, qualification, multi-step forms — instead of normal back-and-forth messages.">mode</Tooltip> is active. See [Custom Components](/widget/custom-components) for the surrounding implementation guidance.

  <Expandable title="ModeComponent">
    <ResponseField name="key" type="string" required>
      The mode's ID, name, or slug.
    </ResponseField>

    <ResponseField name="component" type="(props: ModeComponentProps) => ReturnType<typeof React.createElement>" required>
      The functional component to render.
    </ResponseField>
  </Expandable>

  <Expandable title="ModeComponentProps">
    <ResponseField name="react" type="React">
      The React instance powering the widget. Use this instead of importing your own to avoid version mismatches.
    </ResponseField>

    <ResponseField name="mode" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="id" type="string" />

        <ResponseField name="name" type="string" />

        <ResponseField name="slug" type="string | null" />
      </Expandable>
    </ResponseField>

    <ResponseField name="createStateCheckpoint" type="(payload: Record<string, unknown>) => { success: boolean }">
      Persist state for whatever is happening inside the mode's component.
    </ResponseField>

    <ResponseField name="sendMessage" type="(payload: { content: string; exitModePrompt: string }) => void">
      Call at the end of the mode to submit a summary message and exit the mode.
    </ResponseField>

    <ResponseField name="isSendingMessage" type="boolean">
      Whether `sendMessage` is currently processing. Use to disable the submit button or show a loading indicator after submission.
    </ResponseField>
  </Expandable>

  <Expandable title="example">
    ```tsx theme={"dark"}
    function Onboarding(props: ModeComponentProps) {
      const [monthlyVolume, setMonthlyVolume] = props.react.useState(0);
      const [targetMarket, setTargetMarket] = props.react.useState('');

      const handleSubmit = async () => {
        // Submit to your system here, then end the mode with a summary
        // message and an exit prompt for the AI.
        props.sendMessage({
          content: `My expected volume is ${monthlyVolume} per month.\nMy target market is ${targetMarket}`,
          exitModePrompt: `
            Tell the user something like this:
            "Great! I've now submitted your request.
            We are already checking whether you are eligible to join our service.
            We will notify you as soon as we review your request.
            You can always check the progress by looking at the notifications on the top right of your dashboard."
          `,
        });
      };

      return props.react.createElement(
        'div',
        null,
        props.react.createElement('input', {
          value: monthlyVolume,
          onChange: (e) => setMonthlyVolume(Number(e.target.value)),
        }),
        props.react.createElement('input', {
          value: targetMarket,
          onChange: (e) => setTargetMarket(e.target.value),
        }),
        props.react.createElement(
          'button',
          {
            onClick: handleSubmit,
            disabled: !monthlyVolume || !targetMarket || props.isSendingMessage,
          },
          'Submit',
        ),
      );
    }

    const options = {
      token: '<WIDGET_TOKEN>',
      modesComponents: [
        {
          key: 'onboarding',
          component: Onboarding,
        },
      ],
    };
    ```
  </Expandable>
</ResponseField>

## Accessibility

#### `accessibility`

<ResponseField name="" type="object" default={{}}>
  Accessibility options for the widget UI.

  <Expandable title="properties" defaultOpen>
    <ResponseField name="widgetTriggerButton" type="object">
      <Expandable title="properties" defaultOpen>
        <ResponseField name="label" type="string" default="Chat with us">
          Accessible name for the launcher button. Applied as both `aria-label` (for assistive technology) and `title` (native hover tooltip).

          Required because the trigger is icon-only — satisfies WCAG 2.4.4 (Link Purpose) and 4.1.2 (Name, Role, Value).
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>

  <Expandable title="example">
    ```ts theme={"dark"}
    {
      accessibility: {
        widgetTriggerButton: {
          label: 'Open support chat',
        },
      },
    }
    ```
  </Expandable>
</ResponseField>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Configuration" icon="gear" href="/widget/configuration">
    Job-first guidance — pick the right options for branding, launch flow, and visitor experience.
  </Card>

  <Card title="Playground" icon="flask" href="/widget/playground">
    Try options live and copy a working snippet.
  </Card>

  <Card title="Authentication" icon="shield-check" href="/widget/authentication">
    Identify signed-in visitors and protect conversation history.
  </Card>

  <Card title="Custom Components" icon="paintbrush" href="/widget/custom-components">
    Render richer UI for AI action results and conversation modes.
  </Card>
</CardGroup>
