# Inkeep

## Docs

- [Chat Completions API](https://docs.inkeep.com/ai-api/chat-completions-api)
- [Vercel AI SDK](https://docs.inkeep.com/ai-api/context-mode/vercel-ai-sdk)
- [OpenAI SDK](https://docs.inkeep.com/ai-api/question-answer-mode/openai-sdk)
- [Using `inkeep-qa` models with the Vercel AI SDK](https://docs.inkeep.com/ai-api/question-answer-mode/vercel-ai-sdk)
- [Add analytics to any OpenAI compatible chat](https://docs.inkeep.com/analytics-api/add-analytics-to-any-chat)
- [Delete Conversation](https://docs.inkeep.com/analytics-api/conversation/delete-conversation)
- [Get Conversation](https://docs.inkeep.com/analytics-api/conversation/get-conversation)
- [Get All Conversations](https://docs.inkeep.com/analytics-api/conversation/list-conversations)
- [Log Conversation](https://docs.inkeep.com/analytics-api/conversation/log-conversation): Logs a new conversation or updates an existing one with new messages. Always include all messages.

**API Key Types:** `WEB`, `API`
- [Log Event](https://docs.inkeep.com/analytics-api/events/log-event)
- [Submit Feedback](https://docs.inkeep.com/analytics-api/feedback/submit-feedback)
- [Analytics & Reporting](https://docs.inkeep.com/analytics/reports): Use Inkeep's analytics to keep tabs on usage and get actionable reporting.
- [Add user authentication](https://docs.inkeep.com/customization-guides/authenticate-users): Gate search or chat API calls with authentication.
- [Embedded Support Forms](https://docs.inkeep.com/customization-guides/embedded-support-form): Use our AI annotations to intelligently show a contact support button and integrate with your support system.
- [JavaScript](https://docs.inkeep.com/customization-guides/manage-user-preferences/javascript): Manage end-user usage tracking and privacy settings.
- [React](https://docs.inkeep.com/customization-guides/manage-user-preferences/react): Manage end-user usage tracking and privacy settings.
- [In-App Copilots](https://docs.inkeep.com/customization-guides/product-workflows)
- [Sharable chats](https://docs.inkeep.com/customization-guides/sharable-chats)
- [Style the components](https://docs.inkeep.com/customization-guides/style-components): Customize the search and chat widgets to match your brand.
- [Add your own analytics](https://docs.inkeep.com/customization-guides/use-your-own-analytics): Use our callback function to log events from the search and chat widgets to your own analytics tool.
- [Comparisons](https://docs.inkeep.com/faqs/comparisons): How does Inkeep compare to other AI search and chat services?
- [Features](https://docs.inkeep.com/faqs/features)
- [Projects & Sources](https://docs.inkeep.com/faqs/managing-projects)
- [Pricing](https://docs.inkeep.com/faqs/pricing)
- [Troubleshooting](https://docs.inkeep.com/faqs/troubleshooting)
- [Add AI Chat to your Astro app](https://docs.inkeep.com/integrations/astro/chat-button)
- [Add AI search to your Astro app](https://docs.inkeep.com/integrations/astro/search-bar)
- [Add AI Chat to your Bettermode community](https://docs.inkeep.com/integrations/bettermode)
- [Add an AI chatbot to your Discord server](https://docs.inkeep.com/integrations/discord)
- [Add AI Chat to your Discourse community](https://docs.inkeep.com/integrations/discourse)
- [Add AI Chat to your Document360 docs](https://docs.inkeep.com/integrations/document360/chat-button)
- [Add AI Search & Chat Button to your Document360 docs](https://docs.inkeep.com/integrations/document360/chat-button-and-search-bar)
- [Add AI Search to your Document360 docs](https://docs.inkeep.com/integrations/document360/search-bar)
- [Add AI Chat to your Docusaurus docs](https://docs.inkeep.com/integrations/docusaurus/chat-button)
- [Add AI Search & Chat to your Docusaurus docs](https://docs.inkeep.com/integrations/docusaurus/chat-button-and-search-bar)
- [Add AI Search to your Docusaurus docs](https://docs.inkeep.com/integrations/docusaurus/search-bar)
- [Add AI Chat to your Framer website](https://docs.inkeep.com/integrations/framer/chat-button)
- [Add AI Search to your Framer website](https://docs.inkeep.com/integrations/framer/search-bar)
- [Add Inkeep's UI components to your Fumadocs](https://docs.inkeep.com/integrations/fumadocs/chat-button)
- [Add AI Chat to your Gatsby app](https://docs.inkeep.com/integrations/gatsby/chat-button)
- [Add an Embedded AI Chat to your Gatsby app](https://docs.inkeep.com/integrations/gatsby/embedded-chat)
- [Add AI Search to your Gatsby app](https://docs.inkeep.com/integrations/gatsby/search-bar)
- [Add AI Chat to your GitBook docs](https://docs.inkeep.com/integrations/gitbook/chat-button)
- [Add AI Chat using Google Tag Manager](https://docs.inkeep.com/integrations/google-tag-manager/chat-button)
- [Add AI Chat to your Hugo docs](https://docs.inkeep.com/integrations/hugo/ananke)
- [Add AI Chat to your Docsy docs](https://docs.inkeep.com/integrations/hugo/docsy/chat-button)
- [Add AI Search to your Docsy docs](https://docs.inkeep.com/integrations/hugo/docsy/search-bar)
- [Add Inkeep's UI components to your Mintlify docs](https://docs.inkeep.com/integrations/mintlify)
- [Add AI Chat to your MkDocs docs](https://docs.inkeep.com/integrations/mkdocs/chat-button)
- [Add AI Search & Chat Button to your MkDocs docs](https://docs.inkeep.com/integrations/mkdocs/chat-button-and-search-bar)
- [Add AI Search to your MkDocs docs](https://docs.inkeep.com/integrations/mkdocs/search-bar)
- [Add AI Chat to your Next.js app](https://docs.inkeep.com/integrations/nextjs/chat-button)
- [Add a Custom Trigger to your Next.js app](https://docs.inkeep.com/integrations/nextjs/custom-trigger): Trigger the Inkeep modal with a custom button in your Next.js app.
- [Add an Embedded Chat to your Next.js app](https://docs.inkeep.com/integrations/nextjs/embedded-chat): Add AI chat to your Next.js application.
- [Next 15](https://docs.inkeep.com/integrations/nextjs/next-15): Using the uikit with Next 15 and React 19.
- [Add AI Search to your Next.js app](https://docs.inkeep.com/integrations/nextjs/search-bar)
- [Add AI Chat to your Nextra docs](https://docs.inkeep.com/integrations/nextra/chat-button)
- [Add AI Search & Chat Button to your Nextra docs](https://docs.inkeep.com/integrations/nextra/chat-button-and-search-bar)
- [Add a Custom Trigger to your Nextra docs](https://docs.inkeep.com/integrations/nextra/custom-trigger)
- [Add an Embedded AI Chat to your Nextra docs](https://docs.inkeep.com/integrations/nextra/embedded-chat)
- [Add AI Search to your Nextra docs](https://docs.inkeep.com/integrations/nextra/search-bar)
- [Add AI Chat to your ReadMe docs](https://docs.inkeep.com/integrations/readme/chat-button)
- [Add AI Search & Chat Button to your ReadMe docs](https://docs.inkeep.com/integrations/readme/chat-button-and-search-bar)
- [Add AI Search to your ReadMe docs](https://docs.inkeep.com/integrations/readme/search-bar)
- [Add AI Chat to your Redocly API docs](https://docs.inkeep.com/integrations/redocly/api-docs/chat-button)
- [Add AI Search to Redocly API docs](https://docs.inkeep.com/integrations/redocly/api-docs/search-bar)
- [Add AI Chat to your Redocly developer portal](https://docs.inkeep.com/integrations/redocly/developer-portal/chat-button)
- [Add AI Search to your Redocly developer portal](https://docs.inkeep.com/integrations/redocly/developer-portal/search-bar)
- [Add AI Chat to your Remix app](https://docs.inkeep.com/integrations/remix/chat-button)
- [Add AI Search to your Remix app](https://docs.inkeep.com/integrations/remix/search-bar)
- [Add an AI chatbot to your Slack workspace or community.](https://docs.inkeep.com/integrations/slack)
- [Add AI Chat to your Sphinx docs](https://docs.inkeep.com/integrations/sphinx/chat-button)
- [Add AI Search to your Sphinx docs](https://docs.inkeep.com/integrations/sphinx/search-bar)
- [Add AI Chat to your VitePress docs](https://docs.inkeep.com/integrations/vitepress/chat-button)
- [Add AI Search to your VitePress docs](https://docs.inkeep.com/integrations/vitepress/search-bar)
- [Add AI Chat to your VuePress docs](https://docs.inkeep.com/integrations/vuepress/chat-button)
- [Add an Embedded Chat to your VuePress docs](https://docs.inkeep.com/integrations/vuepress/embedded-chat): Add AI chat to your VuePress docs.
- [Add AI Search to your VuePress docs](https://docs.inkeep.com/integrations/vuepress/search-bar)
- [Add AI Chat to your Webflow site](https://docs.inkeep.com/integrations/webflow/chat-button)
- [Add a Custom Trigger to your Webflow site](https://docs.inkeep.com/integrations/webflow/custom-trigger)
- [Add an Embedded AI Chat to your Webflow site](https://docs.inkeep.com/integrations/webflow/embedded-chat)
- [Add AI Search to your Webflow site](https://docs.inkeep.com/integrations/webflow/search-bar)
- [Add AI Chat to your WordPress site](https://docs.inkeep.com/integrations/wordpress/chat-button)
- [Add AI Search to your WordPress site](https://docs.inkeep.com/integrations/wordpress/search-bar)
- [Add AI Chat to your Zendesk help center](https://docs.inkeep.com/integrations/zendesk/help-center/chat-button)
- [Add AI Search & Chat to your Zendesk help center](https://docs.inkeep.com/integrations/zendesk/help-center/chat-button-and-search-bar)
- [Embedded Chat](https://docs.inkeep.com/integrations/zendesk/help-center/embedded-chat): Add AI chat to your Zendesk Help Center.
- [Add AI Search to your Zendesk help center](https://docs.inkeep.com/integrations/zendesk/help-center/search-bar)
- [AI for Customers](https://docs.inkeep.com/overview/ai-for-customers)
- [AI for Support Teams](https://docs.inkeep.com/overview/ai-for-support-teams)
- [Privacy](https://docs.inkeep.com/overview/privacy): How Inkeep protects your data.
- [Why Inkeep?](https://docs.inkeep.com/overview/why-inkeep)
- [UI Components Release Notes](https://docs.inkeep.com/release-notes/ui-components)
- [Onboarding sources](https://docs.inkeep.com/sources/onboard-new-sources)
- [Updating content](https://docs.inkeep.com/sources/updating-content): How content is kept up to date for any source
- [Copilot for Support Agents (Any Platform)](https://docs.inkeep.com/support-tools/agent-copilot/chrome-extension): Keep, Inkeep's copilot for support agents, can be used with any support platform as a Chrome Extension.
- [Turn closed support tickets into FAQs](https://docs.inkeep.com/support-tools/agent-copilot/draft-faq)
- [Copilot for Support Agents (Zendesk)](https://docs.inkeep.com/support-tools/agent-copilot/zendesk): Install Keep, Inkeep's Copilot for Support Agents, to your Zendesk workspace.
- [Auto-reply to live chat with Inkeep AI](https://docs.inkeep.com/support-tools/auto-reply/live-chat)
- [Auto-reply to support tickets with Inkeep AI](https://docs.inkeep.com/support-tools/auto-reply/tickets)
- [AI Chat](https://docs.inkeep.com/ui-components/common-settings/ai-chat): Customize the chat experience for the Inkeep widgets.
- [Base](https://docs.inkeep.com/ui-components/common-settings/base): Review the basic configuration settings for our search and chat experiences.
- [Modal](https://docs.inkeep.com/ui-components/common-settings/modal): Customize the modal component for Inkeep widgets.
- [Search](https://docs.inkeep.com/ui-components/common-settings/search): Customize search-related settings for the Inkeep widgets.
- [Chat Button (JS)](https://docs.inkeep.com/ui-components/js-snippet/chat-button): Add the Inkeep chat button using a JS snippet.
- [Custom Trigger (JS)](https://docs.inkeep.com/ui-components/js-snippet/custom-trigger)
- [Embedded Chat (JS)](https://docs.inkeep.com/ui-components/js-snippet/embedded-chat): Add a chat UI directly on a dedicated page
- [Search Bar (JS)](https://docs.inkeep.com/ui-components/js-snippet/search-bar): Add the Inkeep search bar using a JS snippet.
- [UI Kit Components](https://docs.inkeep.com/ui-components/overview)
- [Chat Button (React)](https://docs.inkeep.com/ui-components/react/chat-button): Add a chat button React component that triggers the Inkeep modal.
- [Custom Trigger (React)](https://docs.inkeep.com/ui-components/react/custom-trigger)
- [Embedded Chat (React)](https://docs.inkeep.com/ui-components/react/embedded-chat): Add a chat UI directly on a dedicated React powered page.
- [React 17](https://docs.inkeep.com/ui-components/react/react-17): Using the uikit with React 17.
- [Search Bar (React)](https://docs.inkeep.com/ui-components/react/search-bar): Add the Inkeep search bar as a React component.


# Chat Completions API



Inkeep's Chat Completion API endpoints make it easy to develop chatbot or copilot experiences powered by your own knowledge base.

Because these endpoints are compatible with OpenAI's Chat Completion API format, you can use them with most LLM application frameworks, libraries, or SDKs with zero code changes.

For example, you can build a:

*   ChatGPT-like UI using the [Next.js Chatbot template](https://vercel.com/templates/next.js/nextjs-ai-chatbot)
*   Product-specific copilot using the [Vercel AI SDK](https://sdk.vercel.ai/docs/introduction) or [LangChain](https://github.com/langchain-ai/langchainjs)
*   An automation for replying to customer emails or as a plugin to your support platform using [OpenAI's TypeScript SDK](https://github.com/openai/openai-node).

Check out our [examples](https://github.com/inkeep/inkeep-chat-api-examples).

## Available modes

We offer various modes tailored for different use cases.

| Mode Name        | Description                                                                                                                                                                                                           |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inkeep-qa`      | Provides sensible defaults for customer-facing support bot scenarios.                                                                                                                                                 |
| `inkeep-context` | A fully "passthrough" mode that injects Inkeep's RAG context in calls to base models. Best for when you need custom tool (function) calls or full prompting control for custom LLM applications, agents or workflows. |

<Snippet file="InkeepGetIntegrationApiKey.mdx" />

## Using the API

To use with an OpenAI compatible client, simply:

1.  Customize the `baseUrl` to `https://api.inkeep.com/v1`
2.  Specify the mode by setting `model` to a value in the format of `{inkeep-mode}-{model}`

If you'd like to let Inkeep manage which model is used

*   `inkeep-qa-expert`
*   `inkeep-context-expert`

If you'd like to pin the service against a preferred LLM model, use one of:

*   `inkeep-qa-sonnet-3-5` (recommended)
*   `inkeep-qa-gpt-4o`
*   `inkeep-qa-gpt-4-turbo`
*   `inkeep-context-sonnet-3-5` (recommended)
*   `inkeep-context-gpt-4-turbo`
*   `inkeep-context-gpt-4o`

All modes use the OpenAI chat completion API format.

## Question Answer Mode

The `qa` mode is specifically tailored for customer-facing responses like support chatbots or auto-reply workflows.

This mode comes with Inkeep's sensible built-in behavior for:

*   format of citations
*   tone
*   techniques to reduce hallucinations
*   keeping answers scoped to your product and service

QA models are best suited for when you want to develop your own chat UX or automations without worrying about how to prompt or use a model.

The `qa` mode responds with an AI assistant message (`content`) and two additional pieces of information: `links` and `aiAnnotations` (provided as **tools**).

| Component              | Description                                                                                                                                                                                                                                                       |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `content`              | Contains the conversational answer of the AI assistant as any normal OpenAI chat completion request. This is what you would display as the AI assistant answer in a traditional chat UI.                                                                          |
| `provideLinks`         | Provides a list of links (sources) used by the AI assistant to generate a response. You can use this to display citations for an answer.                                                                                                                          |
| `provideAIAnnotations` | Provides labels for the the response, like `answerConfidence`. `answerConfidence` indicates how confident the AI assistant is in its response. You can use this to, for example, conditionally show answers to an end-user only if the AI assistant is confident. |

```ts inkeep-qa-tools-schema.ts
import { z } from "zod";

/* provideLinks tool schema */

const InkeepRecordTypes = z.enum([
    'documentation',
    'site',
    'discourse_post',
    'github_issue',
    'github_discussion',
    'stackoverflow_question',
    'discord_forum_post',
    'discord_message',
    'custom_question_answer',
]);

const LinkType = z.union([
    InkeepRecordTypes,
    z.string() // catch all
]);

const LinkSchema = z.object({
    label: z.string().nullish(), // the value of the footnote, e.g. `1`
    url: z.string(),
    title: z.string().nullish(),
    description: z.string().nullish(),
    type: LinkType.nullish(),
    breadcrumbs: z.array(z.string()).nullish(),
}).passthrough();

export const LinksSchema = z.array(LinkSchema).nullish();

export const LinksToolSchema = z.object({
    links: LinksSchema,
}); 

/* provideAIAnnotations tool schema */

const KnownAnswerConfidence = z.enum([
    'very_confident',
    'somewhat_confident',
    'not_confident',
    'no_sources',
    'other',
]);

const AnswerConfidence = z.union([KnownAnswerConfidence, z.string()]).nullish(); // evolvable

const AIAnnotationsToolSchema = z.object({
    answerConfidence: AnswerConfidence,
}).passthrough();

export const ProvideAIAnnotationsToolSchema = z.object({
    aiAnnotations: AIAnnotationsToolSchema,
});
```

## Context Mode

The `inkeep-context` mode works like a "passthrough" proxy that injects Inkeep's RAG context to a call to an underlying model from Anthropic or OpenAI. These endpoints are fully compatible with all chat completion endpoint functionality like tool calling, JSON mode, image inputs, etc.

This is great for when you're developing a custom AI agent, LLM-application, or workflow and require full control of outputs and custom tool calls but would like a managed RAG system.

This mode is unopinionated and provides a high-degree of flexibility; therefore, it requires a similar level of prompting, testing, and experimentation required of any LLM application.


# Vercel AI SDK



With the Inkeep `context` mode, you can leverage all of the capabilities provided by a normal OpenAI API endpoint but "grounded" with context about your product.

This gives you the full flexibility of creating any LLM application: custom copilots, AI workflows, etc., all backed by your own data. Structured data can be particularly powerful in rendering dynamic UI components as part of a conversational experience.

As a basic example, instead of having a support bot that answers with a single `content` payload, we can instead define a response to be returned as a series of structured steps. The example shown below illustrates how to accomplish that with [streamObject](https://sdk.vercel.ai/docs/reference/ai-sdk-core/stream-object) and Inkeep's `context` model.

```ts index.ts
import { z } from 'zod';

import { streamObject } from 'ai';
import { createOpenAI } from '@ai-sdk/openai'
import dotenv from 'dotenv';
dotenv.config();

if (!process.env.INKEEP_API_KEY) {
  throw new Error('INKEEP_API_KEY is required');
}

const openai = createOpenAI({
  apiKey: process.env.INKEEP_API_KEY,
  baseURL: 'https://api.inkeep.com/v1'
})

const StepSchema = z.object({
  steps: z.array(z.object({
    step: z.string(),
    description: z.string()
  }))
})

async function getResponseFromAI() {

  const result = await streamObject({
    model: openai('inkeep-context-sonnet-3-5'),
    schema: StepSchema,
    messages: [
      {
        role: 'system',
        content:
          'Generate step-by-step instructions to answer the user question about Inkeep only based on the information sources. Break it down to be as granular as possible. Always generate more than one step.'
      },
    ]
  })

    const { partialObjectStream } = result
    for await (const partialObject of partialObjectStream) {
      console.clear();
      console.log(partialObject.steps)
    }

}

getResponseFromAI();

```


# OpenAI SDK



Here's an example of how to use [Chat Completions](https://platform.openai.com/docs/guides/chat-completions) with one of Inkeep's `qa` models

```ts index.ts
import OpenAI from 'openai';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { LinksToolSchema } from './LinksToolSchema';
import dotenv from 'dotenv';
dotenv.config();

if (!process.env.INKEEP_API_KEY) {
  throw new Error('INKEEP_API_KEY is required');
}

const client = new OpenAI({
  baseURL: 'https://api.inkeep.com/v1/',
  apiKey: process.env.INKEEP_API_KEY,
  // dangerouslyAllowBrowser: true, use this setting if you are using this in browser
});

async function getResponseFromAI() {
  const result = await client.chat.completions.create({
    model: 'inkeep-qa-sonnet-3-5',
    messages: [{ role: 'user', content: 'How do I get started with Inkeep?' }],
    stream: true,
    tools: [
      {
        type: 'function',
        function: {
          name: 'provideLinks',
          description: 'Provides links',
          parameters: zodToJsonSchema(LinksToolSchema.extend({
            text: z.string(),
          })),
        },
      },
    ],
    tool_choice: 'auto',
  });

  let toolCalls: any[] = [];
  for await (const chunk of result) {
    const c = chunk.choices[0]
    process.stdout.write(c.delta.content || '');

    if (c.delta?.tool_calls) {
      c.delta.tool_calls.forEach(toolCall => {
        const existingCall = toolCalls[toolCall.index ?? 0];
        if (existingCall) {
          // Update existing call
          if (toolCall.function) {
            existingCall.function = existingCall.function || {};
            if (toolCall.function.name) existingCall.function.name = toolCall.function.name;
            if (toolCall.function.arguments) {
              existingCall.function.arguments = (existingCall.function.arguments || '') + toolCall.function.arguments;
            }
          }
          if (toolCall.type) existingCall.type = toolCall.type;
        } else {
          // Add new call
          toolCalls[toolCall.index ?? 0] = toolCall;
        }
      });
    }
  }

  // Check if the function is called in the response
  if (toolCalls && toolCalls.length > 0) {
    const provideLinksCall = toolCalls.find(call => call.function.name === 'provideLinks');
    if (provideLinksCall) {
      const functionArgs = JSON.parse(provideLinksCall.function.arguments);
      await provideLinks(functionArgs);
    } else {
      console.log('No provideLinks tool call found');
    }
  }
}

const provideLinks = async ({ links, text }: { links?: (typeof LinksToolSchema._type)['links']; text: string }) => {
  console.log('\nLinks used in response: ', links);

  return Promise.resolve();
};

getResponseFromAI();

```


# Using `inkeep-qa` models with the Vercel AI SDK



## Using useChat and a route

[`useChat`](https://sdk.vercel.ai/docs/reference/ai-sdk-ui/use-chat) is cross-platform hook that helps you create a conversational experience with the Vercel AI SDK with React, Svelte, Vue, and Solid frontends.

To use useChat, first create an API route using Next.js. Here's where we'll call Inkeep.

### API Route

```tsx app/api/chat/route.ts
import { LinksTool } from '@/lib/chat/inkeep-qa-schema'
import { createOpenAI } from '@ai-sdk/openai'
import { streamText } from 'ai'

export const runtime = 'edge'

const openai = createOpenAI({
  apiKey: process.env.INKEEP_API_KEY,
  baseURL: 'https://api.inkeep.com/v1'
})

export async function POST(req: Request) {
  const reqJson = await req.json()

  const result = await streamText({
    model: openai('inkeep-qa-sonnet-3-5'),
    messages: reqJson.messages.map((message: any) => ({
      role: message.role,
      content: message.content,
      name: 'inkeep-qa-user-message',
      id: message.id
    })),
     tools: { // to get the citation information
      provideLinks: {
        ...LinksTool
      }
    },
    toolChoice: 'auto'
  })

  return result.toAIStreamResponse()
}
```

### Client

```tsx app/page.tsx
'use client'

import { useChat } from 'ai/react'

export default function Page() {
  const { messages, isLoading, input, handleSubmit, handleInputChange } =
    useChat({
      streamMode: 'stream-data',
      sendExtraMessageFields: true,
      onResponse(response) {
        if (response.status === 401) {
          console.error(response.statusText)
        }
      }
    })

  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.role === 'user' ? 'User: ' : 'AI: '}
          {message.content}
        </div>
      ))}

      {isLoading && <div>Loading...</div>}

      <form onSubmit={handleSubmit}>
        <input name="prompt" value={input} onChange={handleInputChange} />
        <button type="submit">Submit</button>
      </form>
    </>
  )
}
```

## Using Server Actions (AI SDK Actions)

[`streamUI`](https://sdk.vercel.ai/docs/reference/ai-sdk-rsc/stream-ui) is another way to use the Vercel AI SDK, but with React Server Components. This lets you stream entire UI components, not just the text that is then parsed and rendered on the client.

This example illustrates how to render assistant messages and a "sources" list provided by the `provideLinks` tool.

### Server Action

<CodeGroup>
  ```tsx lib/chat/actions.ts
  import 'server-only'

  import { createAI, getMutableAIState, streamUI } from 'ai/rsc'
  import { createOpenAI } from '@ai-sdk/openai'

  import { Message } from 'ai'
  import { nanoid } from './utils'
  import { LinksTool } from './inkeep-qa-schema'

  const openai = createOpenAI({
    apiKey: process.env.INKEEP_API_KEY,
    baseURL: 'https://api.inkeep.com/v1'
  })

  async function submitUserMessage(content: string) {
    'use server'

    const aiState = getMutableAIState<typeof AI>()

    aiState.update({
      ...aiState.get(),
      messages: [
        ...aiState.get().messages,
        {
          id: nanoid(),
          role: 'user',
          content
        }
      ]
    })

    const answerMessageId = nanoid()

    const result = await streamUI({
      model: openai('inkeep-qa-sonnet-3-5'),
      messages: [
        ...aiState.get().messages.map((message: any) => ({
          role: message.role,
          content: message.content,
          name: 'inkeep-qa-user-message',
          id: message.id
        }))
      ],
      text: ({ content }) => {
        const assistantAnswerMessage = {
          id: answerMessageId,
          role: 'assistant',
          content,
          name: 'inkeep-qa-assistant-message'
        } as Message

        const currentMessages = aiState.get().messages
        const lastMessage = currentMessages[currentMessages.length - 1]

        aiState.update({
          ...aiState.get(),
          messages:
            lastMessage?.id === answerMessageId
              ? [...currentMessages.slice(0, -1), assistantAnswerMessage]
              : [...currentMessages, assistantAnswerMessage]
        })

        return <div>{assistantAnswerMessage.content}</div>
      },
      tools: {
        provideLinks: {
          ...LinksTool,
          generate: async ({ links }) => { // render sources UI once tool is complete
            const currentMessages = aiState.get().messages
            const lastMessage = currentMessages[currentMessages.length - 1]
            const lastMessageWithToolResults = {
              ...lastMessage,
              toolInvocations: [
                {
                  toolName: 'provideLinks',
                  result: links
                }
              ]
            } as Message

            aiState.done({
              ...aiState.get(),
              messages: [
                ...currentMessages.slice(0, -1),
                lastMessageWithToolResults
              ]
            })

            return <div>{lastMessageWithToolResults.content}</div>
          }
        }
      },
      toolChoice: 'auto'
    })

    return {
      id: nanoid(),
      display: result.value
    }
  }

  export type AIState = {
    chatId: string
    messages: Message[]
  }

  export type UIState = {
    id: string
    display: React.ReactNode
  }[]

  export const AI = createAI<AIState, UIState>({
    actions: {
      submitUserMessage
    },
    initialUIState: [],
    initialAIState: { chatId: nanoid(), messages: [] }
  })
  ```

  ## Page.tsx

  ```tsx app/page.tsx
  import { Chat } from '@/components/chat'
  import { AI } from '@/lib/chat/actions'
  import { nanoid } from '@/lib/chat/utils'

  export default async function IndexPage() {
    const id = nanoid()

    return (
      <AI initialAIState={{ chatId: id, messages: [] }}>
        <Chat id={id} />
      </AI>
    )
  }
  ```
</CodeGroup>

### Client

```tsx app/components/chat.tsx
'use client'

import { useUIState, useAIState, useActions } from 'ai/rsc'
import { Message } from 'ai'
import { UIState } from '@/lib/chat/actions'
import { nanoid } from '@/lib/chat/utils'

export interface ChatProps extends React.ComponentProps<'div'> {
  initialMessages?: Message[]
  id?: string
}

export function Chat({ id, className }: ChatProps) {
  const [messages] = useUIState()

  return (
    <div className="group w-full overflow-auto pl-0 peer-[[data-state=open]]:lg:pl-[250px] peer-[[data-state=open]]:xl:pl-[300px]">
      <div className={'pb-[200px] pt-4 md:pt-10'}>
        <ChatList messages={messages} />
        <div className="w-full h-px" />
      </div>
    </div>
  )
}

export interface ChatList {
  messages: UIState
}

export function ChatList({ messages }: ChatList) {
  const [messagesUIState, setMessagesUIState] = useUIState()
  const { submitUserMessage } = useActions()

  const handleSubmit = async (e: any) => {
    e.preventDefault()

    const form = e.currentTarget
    const input = form.elements.namedItem('prompt') as HTMLInputElement

    const value = input.value.trim()
    if (!value) return

    input.value = '' // Clear the input after submission

    const userMessage = {
      id: nanoid(),
      content: value,
      role: 'user'
    } as Message

    // Optimistically add user message UI
    setMessagesUIState(currentMessages => [
      ...currentMessages,
      {
        id: nanoid(),
        display: <div>{userMessage.content}</div>
      }
    ])

    // Submit and get response message
    const responseMessage = await submitUserMessage(value)
    setMessagesUIState(currentMessages => [...currentMessages, responseMessage])
  }

  return (
    <>
      <div className="relative mx-auto max-w-2xl px-4">
        {messages.map(message => (
          <div key={message.id}>{message.display}</div>
        ))}
      </div>

      <form onSubmit={handleSubmit}>
        <input name="prompt" />
        <button type="submit">Submit</button>
      </form>
    </>
  )
}
```


# Add analytics to any OpenAI compatible chat



With Inkeep's Analytics API, you can log your AI chat conversations to:

1.  Get Inkeep's analytics and reporting for the chats
2.  Provide and track "Thumbs up/down" feedback
3.  Power "Share" or "Saved Chats" experiences

The Inkeep Analytics API is designed to be flexible and can log any OpenAI-compatible conversation, including those with tool calls or custom functionality.

If you're using [Inkeep's Chat API](/ai-api/chat-completions-api) to create your own chat UX or custom copilot, you should use the Analytics API to enable the same reporting as provided by the `@inkeep/uikit` component library.

<Snippet file="InkeepGetIntegrationApiKey.mdx" />

## Log a conversation

### Initial message

To log a new conversation, make a POST request to the `/conversations` endpoint.

```javascript
const initialConversationData = {
  id: "conv_1234", // optional
  type: "openai",
  messages: [
    {
      role: "user",
      content: "Can I log any OpenAI-compatible conversation?"
    },
    {
      role: "assistant",
      content: "Yes, our analytics engine is agnostic. You can log full conversations of your agent or custom copilot, even if not all interactions hit Inkeep's API endpoint."
    }
  ],
  properties: {
    // Add any custom properties here
  },
  userProperties: {
    // Add any user-specific properties here
  }
};

let currentConversation = await fetch('https://api.analytics.inkeep.com/conversations', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(initialConversationData)
}).then(response => {
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response.json();
});

console.log(currentConversation);
```

### Follow-up messages

To log additional messages in an existing conversation, include the `id` returned from the initial submission:

```javascript
const continueConversationData = {
  id: currentConversation.id,
  type: "openai",
  messages: [
    ...currentConversation.messages,
    // Add new messages
    {
      role: "user",
      content: "Does that mean it supports logging tool calls?"
    },
    {
      role: "assistant",
      content: "Yup, Inkeep's Analytics API is fully compatible with OpenAI chat completions endpoints, including tool calling."
    }
  ]
};

currentConversation = await fetch('https://api.analytics.inkeep.com/conversations', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(continueConversationData)
}).then(response => {
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response.json();
});

console.log(currentConversation);
```

<Note>You can provide your own IDs for conversations and messages, or Inkeep will auto-generate them. For logging continuous exchanges in a conversation, make sure to retain and use the same conversation ID and always include the entire message history. `POST /conversation` works like an upsert operation.</Note>

## Submit feedback

Use the `/feedback` endpoint to log 👍 / 👎 feedback from your UI.

```javascript
const feedbackData = {
  type: "positive", // or "negative"
  messageId: "{assistant_message_id}", // e.g. currentConversation.messages[1].id
  reasons: [
    {
      label: "accurate_code_snippet",
      details: "The code worked perfectly."
    }
  ],
  userProperties: {
    // Add any user-specific properties here
  }
};

fetch('https://api.analytics.inkeep.com/feedback', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(feedbackData)
})
.then(response => response.json())
.then(data => console.log(data));
```

## Monitor

To view analytics for your logged conversations and feedback:

1.  Go to [portal.inkeep.com](https://portal.inkeep.com)
2.  Navigate to **Projects** and select the relevant one
3.  View the **Chat Sessions**, **Insights**, and **Analytics** tabs

## Use for "Share" or "Saved Chats"

You can use the `GET /conversations` endpoint to retrieve logged conversations and power "Share" or "Saved Chats" features.

Here's an example of how to fetch a specific conversation:

```ts
fetch('https://api.analytics.inkeep.com/conversations/{existing_conversation_id}', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
})
.then(response => response.json())
.then(data => {
  // Use the conversation data to rehydrate your UI
  console.log(data);
});
```

The response will contain detailed information about the conversation:

```json
{
  "id": "conv_123456789",
  "type": "openai",
  "createdAt": "2024-07-11T10:15:30.123Z",
  "updatedAt": "2024-07-11T10:16:45.678Z",
  "projectId": "proj_987654321",
  "integrationId": "intg_246813579",
  "properties": {
    "source": "web_chat"
  },
  "userProperties": {
    "userId": "user_135792468"
  },
  "messages": [
    {
      "id": "msg_1",
      "type": "openai",
      "conversationId": "conv_123456789",
      "createdAt": "2024-07-11T10:15:30.123Z",
      "updatedAt": "2024-07-11T10:15:30.123Z",
      "role": "user",
      "content": "Can I log any OpenAI-compatible conversation?",
      "name": null,
      "links": null,
      "properties": {},
      "userProperties": {}
    },
    {
      "id": "msg_2",
      "type": "openai",
      "conversationId": "conv_123456789",
      "createdAt": "2024-07-11T10:15:40.456Z",
      "updatedAt": "2024-07-11T10:15:40.456Z",
      "role": "assistant",
      "content": "Yes, our analytics engine is agnostic. You can log full conversations of your agent or custom copilot, even if not all interactions hit Inkeep's API endpoint.",
      "name": null,
      "links": null,
      "properties": {},
      "userProperties": {}
    },
    {
      "id": "msg_3",
      "type": "openai",
      "conversationId": "conv_123456789",
      "createdAt": "2024-07-11T10:16:15.789Z",
      "updatedAt": "2024-07-11T10:16:15.789Z",
      "role": "user",
      "content": "Does that mean it supports logging tool calls?",
      "name": null,
      "links": null,
      "properties": {},
      "userProperties": {}
    },
    {
      "id": "msg_4",
      "type": "openai",
      "conversationId": "conv_123456789",
      "createdAt": "2024-07-11T10:16:45.678Z",
      "updatedAt": "2024-07-11T10:18:45.678Z",
      "role": "assistant",
      "content": "Yup, Inkeep's Analytics API is fully compatible with OpenAI chat completion endpoints, including tool calling.",
      "name": null,
      "links": null,
      "properties": {},
      "userProperties": {}
    }
  ],
  "messagesOpenAIFormat": [
    {
      "role": "user",
      "content": "Can I log any OpenAI-compatible conversation?"
    },
    {
      "role": "assistant",
      "content": "Yes, Inkeep's analytics engine is agnostic. You can log full conversations of your agent or custom copilot, even if not all interactions hit Inkeep's API endpoint."
    },
    {
      "role": "user",
      "content": "Does that mean it supports logging tool calls?"
    },
    {
      "role": "assistant",
      "content": "Yup, Inkeep's Analytics API is fully compatible with OpenAI chat completions endpoints, including tool calling."
    }
  ]
}
```

This response includes all extended details about the conversation and its messages, including any properties you associated with it.

The `messagesOpenAIFormat` property provides the messages of the conversation in a format compatible with OpenAI's chat completions API. You can use this to implement **Share a chat**, **Saved Chats**, or any other functionality that requires retrieving an existing chat.

Here's how:

*   **Share a chat** - Create an entry point in your application or UI that parses a query parameter, e.g. `?chatId=<id>`. Then, use the `GET /conversations/{id}` endpoint to get the last state of a conversation and hydrate the chat UI in your own application. If you want to be able to "fork" shared chats, then simply use a different `id` when logging the continuation of a shared conversation to `POST /conversations`. This will log it as a separate conversation from the original.
*   **Saved Chats** - Whenever a user creates a new conversation, associate that `id` with the user in your database or application. This way, you have a relationship of `chats<>user` and can display a user's history in your UI. You can also store that relationship in a user's browser session for unauthenticated contexts.

## Log usage events

In addition to logging conversations and feedback, you can also track specific usage events related to conversations or messages using the `/events` endpoint.

For example, you may want to track events like:

*   `message:copied`: When a user copies a message
*   `message:code_snippet:copied`: When a user runs a code snippet from a message
*   `conversation:shared`: When a user shares a conversation

When logged, they will be incorporated in the Inkeep Analytics reporting.

The below shows an example of how to log these events.

```js log-event.js
// Message event
const eventData = {
  type: "message:copied",
  entityType: "message" // 'message' or 'conversation'
  messageId: "{message_id}",
};

fetch('https://api.analytics.inkeep.com/events', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(eventData)
})
.then(response => response.json())
.then(data => console.log(data));

// Conversation Event
const conversationEventData = {
  type: "conversation:shared",
  entityType: "conversation"
  conversationId: "{conversation_id}",
};

fetch('https://api.analytics.inkeep.com/events', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(conversationEventData)
})
.then(response => response.json())
```


# Delete Conversation

delete /conversations/{id}



# Get Conversation

get /conversations/{id}



# Get All Conversations

get /conversations



# Log Conversation

post /conversations
Logs a new conversation or updates an existing one with new messages. Always include all messages.

**API Key Types:** `WEB`, `API`



# Log Event

post /events



# Submit Feedback

post /feedback



# Analytics & Reporting

Use Inkeep's analytics to keep tabs on usage and get actionable reporting.

<Note> Prefer a visual walkthrough? Check out [this video](https://vimeo.com/1010449025/54aade446b?share=copy).</Note>

## Chat Sessions

With the **Chat Sessions** table you get a granular view of all conversations users are having with your AI assistant.

You can:

*   **View full conversations** - view all chats in full, including sources used and all messages.
*   **Review Thumbs Up / Thumbs down** - review conversations users gave feedback on.
*   **Use AI filters** - drill down on questions where there wasn't supporting content.
*   **Export as CSV** - download all question-answer pairs.

<img className="block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/analytics/chat-sessions.png" />

## Insights (AI Reports)

In **Insights**, you get weekly and monthly reports that use AI to provide actionable insights for your documentation, support, and product teams.

Each report includes concise summaries of:

1.  **Prioritized themes** - top features or capabilities that users mention.
2.  **Content gaps** - cases where Inkeep wasn't able to find supporting content.
3.  **Feature gaps** - cases where a user asked for a feature that wasn't supported.
4.  **Third-party mentions** - any third party software, frameworks, or services that users ask about in relation to your product.

<img className="block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/analytics/insights.png" />

## Analytics

In **Analytics** you can see the key statistics about how users engage with your AI assistant.

Metrics include:

*   **Total chat messages**
*   **Chat sessions**
*   **Unique users**
*   **Thumbs up vs. Thumbs down**
*   **Chat messages over time**
*   **Code snippets copied**
*   **Chat messages copied**
*   **Shared chats**
*   **New chat sessions**

<img className="block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/analytics/analytics.png" />

Want to correlate AI chat usage with your own user funnels or BI stack? Follow [this guide](/customization-guides/use-your-own-analytics).


# Add user authentication

Gate search or chat API calls with authentication.

Sometimes, you may want to authenticate users accessing your search or chat service.

This is typically applicable if you'd like to:

*   Restrict answers of the bot to content that a user has access to. Typically applicable for products with private, gated documentation.
*   Provide context (information) about the user or apply source filters in a way that cannot be tampered.
*   Implement your own CAPTCHA, throttling, or anti-bot security measures and use a JWT to verify that the user has passed it.

## Generate verification keys

Generate a private and public key pair using `openssl` or the method of your choice.

```bash
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
```

The *private* key will be used by your backend service to mint JWT tokens that are sent to the Inkeep search and chat service. It should not be shared with any other service or third party.

## Configure the integration

In your Inkeep Integration, configure the following under **Advanced Settings**:

*   **Require user authentication**: set to `true`
*   **Public key**: provide the value of `public_key.pem`
*   **Issuer**: the value we should ensure matches the `iss` claim in the authentication token. Example: `https://auth.mydomain.com`. This is just a string URI so can be arbitrary. It just needs to uniquely identify your service.

We will use the public key to validate the token that is provided to our service was created ('minted') by your service. We check the signature of the token and the `iss`, `exp`, `aud` and other standard claims.

## Generating a token in your backend

You need a Microservice to create a JWT token every time a user is looking to make a search or chat.

Here is an example of how to generate a token in Node.js:

```typescript
import fs from "fs";
import jwt from "jsonwebtoken";

interface TokenConfiguration {
  iss: string;
  aud: string;
  integrationId: string;
  filters?: { // optional
    attributes: {
      $and: Array<{
        [key: string]: string | { $in: string[] };
      }>;
    };
  };
  userAttributes?: { // optional
    [key: string]: string | number | string[];
  };
  expirationOffsetHours?: number;
}

const generateToken = ({
  iss,
  aud,
  integrationId,
  filters,
  userAttributes,
  expirationOffsetHours = 24,
}: TokenConfiguration): string => {
  const privateKey = fs.readFileSync("private_key.pem", "utf8"); // your private key

  const payload = {
    iss,
    aud: "https://api.inkeep.com",
    integrationId,
    filters,
    userAttributes,
    exp: Math.floor(Date.now() / 1000) + expirationOffsetHours * 60 * 60,
  };

  const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });

  return token;
};

const exampleConfiguration: TokenConfiguration = {
  iss: "https://auth.mydomain.com",
  integrationId: "inkeep-integration-id",
  filters: {
    attributes: {
      $and: [
        {
          env: "dev",
        },
        {
          modules: {
            $in: ["module1", "module2"],
          },
        },
      ],
    },
  },
  userAttributes: {
    userId: "123456789",
    userEmail: "me@inkeep.com",
    userCohorts: [`group1`, `group2`],
  },
  expirationOffsetHours: 24,
};

const token = generateToken(exampleConfiguration);
```

The token should include the following claims:

*   `iss`: The issuer of the token. Example `https://auth.mydomain.com`.
*   `aud`: The audience of the token. It should always be `https://api.inkeep.com`.
*   `integrationId`: The ID of the integration. This must match the integration ID in the body of the request.
*   `filters` (optional): Same as documented in the [Start new chat](/inkeep-api/create-new-chat-session#chatfiltersinput) request body. Takes precedence over the `filters` value provided in the request body.
*   `userAttributes` (optional): Same as documented in the [Start new chat](/inkeep-api/create-new-chat-session#newsessionchatresultinput) request body. Takes precedence over the `userAttributes` value provided in the request body.
*   `exp`: The expiration time of the token, in seconds since the epoch.

## Add the token to chat or search requests

### When using the Inkeep widgets

Pass in the token you mint to the widget configuration as `baseSettings.userToken`. See [here](/ui-components/common-settings/base).

### When using the Inkeep API

Make sure to include these two headers:

*   `Authorization` : `Bearer {integrationApiKey}` (must still be present in all requests)
*   `User-Token`: `{JWT_TOKEN_SIGNED_BY_YOUR_BACKEND}`


# Embedded Support Forms

Use our AI annotations to intelligently show a contact support button and integrate with your support system.

## Scenario

If the bot was not able to answer a user's question, you can optionally show a contact support button which can be configured to either open a customizable form or call a custom callback function. The form can be configured to collect whatever information you would like and you may optionally include information about the user's chat session which can then be turned into a support ticket. The callback function can be used to initiate a live chat.

<img src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/support-workflows/support-workflow.gif" />

## Example

```typescript
import { type InkeepAIChatSettings, type FormConfig } from "@inkeep/uikit";

const supportFormConfig: FormConfig = {
  heading: "Contact support",
  fields: [
    {
      type: "STANDARD_FIELD",
      label: "Name",
      name: "first_name",
      inputType: "TEXT",
    },
    {
      type: "STANDARD_FIELD",
      label: "Email",
      name: "email",
      inputType: "EMAIL",
      required: true,
    },
    {
      type: "INCLUDE_CHAT_SESSION",
      defaultValue: true,
    },
    {
      type: "STANDARD_FIELD",
      label: "Additional details",
      name: "additional_details",
      inputType: "TEXTAREA",
    },
    {
      type: 'STANDARD_FIELD',
      label: 'Category',
      name: 'category',
      inputType: 'SELECT',
      selectOptions: [
        { label: 'Bug', value: 'BUG' },
        { label: 'Feature idea', value: 'FEATURE' },
        { label: 'Account access', value: 'ACCOUNT' },
      ],
    },
  ],
  submitCallback: (values) => {
    // replace with your own api call to create a support ticket
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({ success: true });
      }, 2000);
    });
  },
};

const inkeepAIChatSettings: InkeepAIChatSettings = {
  //... rest of inkeepAIChatSettings
  includeAIAnnotations: {
    shouldEscalateToSupport: true,
  },
  aiAnnotationPolicies: {
    shouldEscalateToSupport: [
      {
        threshold: "STANDARD", // "STRICT" or "STANDARD"
        action: {
          type: "SHOW_SUPPORT_BUTTON",
          label: "Contact Support",
          icon: { builtIn: "LuUsers" },
          action: {
            type: "OPEN_FORM",
            formConfig: supportFormConfig,
          },
        },
      },
    ],
  },
};
```

## IncludeAIAnnotations

This is currently an enterprise preview feature.

| Property                | Type      | Description                                                         |
| ----------------------- | --------- | ------------------------------------------------------------------- |
| shouldEscalateToSupport | `boolean` | Whether or not to include the `shouldEscalateToSupport` annotation. |

## AIAnnotationPolicies

Be sure to include the corresponding property in `includeAIAnnotations`.

| Property                | Type                              | Description                             |
| ----------------------- | --------------------------------- | --------------------------------------- |
| shouldEscalateToSupport | `ShouldEscalateToSupportPolicy[]` | Policies for `shouldEscalateToSupport`. |

### ShouldEscalateToSupportPolicy

| Property  | Type                                | Description                                                                                                                            |
| --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| threshold | `"STANDARD"` \| `"STRICT"`          | Support threshold. `"STRICT"` indicates that the bot is very confident about support escalation while `"STANDARD"` is lower threshold. |
| action    | `ShowEscalateToSupportButtonAction` | Action to take when support escalation threshold is met.                                                                               |

### ShowEscalateToSupportButtonAction

| Property | Type                                       | Description                                                                                                         |
| -------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| type     | `"SHOW_SUPPORT_BUTTON"`                    | Type of action.                                                                                                     |
| label    | `string`                                   | Button label.                                                                                                       |
| icon     | `CustomIcon`                               | Icon that goes next to the button label. See [here](/ui-components/common-settings/ai-chat#customicon) for details. |
| action   | `OpenFormAction` \| `InvokeCallbackAction` | Type of action to take when button is clicked.                                                                      |

#### OpenFormAction

| Property   | Type          | Description                                                     |
| ---------- | ------------- | --------------------------------------------------------------- |
| type       | `"OPEN_FORM"` | Type of action.                                                 |
| formConfig | `FormConfig`  | Custom form configuration. See [here](#formconfig) for details. |

#### InvokeCallbackAction

| Property         | Type                                 | Description                                                                                      |
| ---------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------ |
| type             | `"INVOKE_CALLBACK"`                  | Type of action.                                                                                  |
| callback         | `(args: InvokeCallbackArgs) => void` | Callback to be invoked when the button is clicked. See [here](#invokecallbackargs) for details.  |
| shouldCloseModal | `boolean`                            | Determines whether the chat modal should close when the callback is invoked. Default is `false`. |

#### InvokeCallbackArgs

| Property      | Type        | Description                     |
| ------------- | ----------- | ------------------------------- |
| chatSessionId | `string`    | Id for the chat session.        |
| messages      | `Message[]` | Messages from the chat session. |

<Snippet file="FormConfig.mdx" />


# JavaScript

Manage end-user usage tracking and privacy settings.

<Snippet file="ManageUserPrivacyApiReference.mdx" />

In the example below we have a single checkbox where the user can opt out of all analytics. This pattern can be used more granularly to map your own customer preference system to ours, for example for cookie and tracking consent preferences.

<Note> By default, Inkeep does not collect IP addresses, device IDs, or other device-based information from components of the Inkeep `uikit` library.</Note>

## Example

JavaScript example:

```javascript
// instantiate inkeepWidget using Inkeep.embed()
// be sure to set any necessary initial values for optOutAnalyticalCookies, optOutAllAnalytics, optOutFunctionalCookies, remoteErrorLogsLevel if the user's preference is already known
// ...

const optOutAnalyticsInput = document.getElementById("optOutAnalytics");

// event listener for when user's preference changes
optOutAnalyticsInput.addEventListener("change", (e) => {
  const shouldOptOut = e.target.checked;

  embeddedChat.render({
    baseSettings: {
      optOutAnalyticalCookies: shouldOptOut,
      optOutAllAnalytics: shouldOptOut,
      optOutFunctionalCookies: shouldOptOut,
      remoteErrorLogsLevel: shouldOptOut
        ? 0 // None
        : 2, // Identifiable Errors
    },
  });
});
```

HTML example:

```html
<input type="checkbox" id="optOutAnalytics" />
<label for="optOutAnalytics">Opt out of all analytics</label>
```


# React

Manage end-user usage tracking and privacy settings.

<Snippet file="ManageUserPrivacyApiReference.mdx" />

In the example below we have a single checkbox where the user can opt out of all analytics. This pattern can be used more granularly to map your own customer preference system to ours.

<Note> By default, Inkeep does not collect IP addresses, device IDs, or other device-based information from components of the Inkeep `uikit` library.</Note>

## Example

Parent component:

```tsx
import { ChangeEvent, useState } from "react";
import InkeepChatButton from "./components/InkeepChatButton";
import { RemoteErrorLogsLevel } from "@inkeep/uikit";

function ParentComponent() {
  const [optOutAnalytics, setOptOutAnalytics] = useState(false);

  const handleChangeCheckbox = (e: ChangeEvent<HTMLInputElement>) => {
    setOptOutAnalytics(e.target.checked);
  };

  return (
    <>
      <InkeepChatButton
        inkeepPrivacyProperties={{
          optOutAnalyticalCookies: optOutAnalytics,
          optOutAllAnalytics: optOutAnalytics,
          optOutFunctionalCookies: optOutAnalytics,
          remoteErrorLogsLevel: optOutAnalytics
            ? RemoteErrorLogsLevel.None
            : RemoteErrorLogsLevel.IdentifiableErrors,
        }}
      />
      {/* ... */}
      <input
        type="checkbox"
        checked={optOutAnalytics}
        id="optOutAnalytics"
        onChange={handleChangeCheckbox}
      />
      <label htmlFor="optOutAnalytics">Opt out of all analytics</label>
      {/* ... */}
    </>
  );
}

export default ParentComponent;
```

Widget component:

```tsx
import {
  type InkeepChatButtonProps,
  type InkeepBaseSettings,
  type RemoteErrorLogsLevel,
} from "@inkeep/uikit";
import { memo, useEffect, useState } from "react";

const baseSettings: InkeepBaseSettings = {
  apiKey: process.env.INKEEP_API_KEY!,
  integrationId: process.env.INKEEP_INTEGRATION_ID!,
  organizationId: process.env.INKEEP_ORGANIZATION_ID!,
  organizationDisplayName: "Inkeep",
  primaryBrandColor: "#26D6FF",
};

interface InkeepPrivacyProperties {
  optOutAnalyticalCookies: boolean;
  optOutAllAnalytics: boolean;
  optOutFunctionalCookies: boolean;
  remoteErrorLogsLevel: RemoteErrorLogsLevel;
}

interface IProps {
  inkeepPrivacyProperties: InkeepPrivacyProperties;
}

function InkeepChatButton({ inkeepPrivacyProperties }: IProps) {
  const [ChatButton, setChatButton] =
    useState<React.FC<InkeepChatButtonProps>>();

  useEffect(() => {
    const loadChatButton = async () => {
      try {
        const { InkeepChatButton } = await import("@inkeep/uikit");
        setChatButton(() => InkeepChatButton);
      } catch (error) {
        console.error("Failed to load ChatButton:", error);
      }
    };

    loadChatButton();
  }, []);

  const chatButtonProps: InkeepChatButtonProps = {
    baseSettings: {
      ...baseSettings,
      ...inkeepPrivacyProperties,
    },
    aiChatSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    modalSettings: {
      // optional settings
    },
  };

  return ChatButton ? <ChatButton {...chatButtonProps} /> : <></>;
}

// use memo to avoid component re-rendering
export default memo(InkeepChatButton);
```


# In-App Copilots



In-App Copilots provide intelligent, context-aware assistance directly within your application, enhancing user experience and streamlining interactions. Use [context and guidance](/customization-guides/product-workflows#context-and-guidance) to provide dynamic user or product specific information or [workflows](/customization-guides/product-workflows#workflows) to guide users through complex tasks or common scenarios.

## Context and guidance

Context and guidance can be passed in via the [aiChatSettings](/ui-components/common-settings/ai-chat).

<Snippet file="AIChatContext.mdx" />

<Snippet file="AIChatGuidance.mdx" />

## Workflows

Workflows allow you to create app-specific flows that are specific to common scenarios your users may face. This often maps to specific tasks in your onboarding or user activation funnel.

Workflows support:

*   A custom initial message
*   Specifying clarifying questions or information the bot should ask for
*   Custom attachment types that can invoke forms, modals, or API calls within your front-end application to gather user-specific information

### Example

```ts example.ts
import { type Workflow } from "@inkeep/uikit";
import { type InkeepAIChatSettings } from "@inkeep/uikit";

const integrateWithMyAppWorkflow: Workflow = {
  id: "ikp_integrate_with_my_app",
  displayName: "Help me integrate with my app",
  goals: [
    "Identify the platform that the user is looking to add Inkeep on. Can be a website-based platform or docs platform, Slack/Discord bots, or via API",
    "Give instructions for how to add the bot to that platform."
  ],
  botPersona: "You are an expert solutions engineer",
  informationToCollect: [
    {
      description: "Platform they are integrating Inkeep to",
      required: true,
    },
  ],
  guidance: [
    "If not clear from the user's message, ask clarifying questions to understand the underlying platform (e.g. Docusaurus vs Slack vs ReadMe, etc.).", 
    "If the platform is not detailed in our quickstarts, then ask whether it's a React or JavaScript based platform so you can give the right guidance based on that.",
  ],
  initialReplyMessage:
    "Happy to help. \n Where are you looking to add Inkeep?",
};

//...rest of your settings
const aiChatSettings: InkeepAIChatSettings = {
  //... rest of aiChatSettings
  workflows: [integrateWithMyAppWorkflow]
};
```

## Workflow

| Property             | Type                                                           | Description                                                                                                                             |
| -------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| id                   | `string`                                                       | **Required**. Id of the workflow.                                                                                                       |
| displayName          | `string`                                                       | **Required**. Label of workflow in the UI.                                                                                              |
| goals                | `string[]`                                                     | **Required**. Goals for the workflow, not visible to the user.                                                                          |
| informationToCollect | `WorkflowInformationToCollect[]`                               | **Required**. Information to collect from the user. [Learn more](/customization-guides/product-workflows#workflowinformationtocollect). |
| botPersona           | `string`                                                       | The persona of the bot useful for defining the character, tone, and interaction style the bot will adopt when communicating with users. |
| context              | `string[]`                                                     | Additional context about this workflow for the LLM, not visible to the user.                                                            |
| guidance             | `string[]`                                                     | Additional guidance for the LLM, not visible to the user.                                                                               |
| initialReplyMessage  | `string`                                                       | **Required**. The reply message from the bot after the user selects a workflow.                                                         |
| supportedInputs      | `(WorkflowModalSingleInput \| WorkflowFunctionalMultiInput)[]` | Configuration for the workflow inputs. [Learn more](/customization-guides/product-workflows#workflowmodalsingleinput).                  |

### WorkflowInformationToCollect

| Property    | Type      | Description                                                                                                              |
| ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------ |
| description | `string`  | **Required**. Description of the information to be collected from the user in order to assist them through the workflow. |
| required    | `boolean` | **Required**. Whether or not this information is required.                                                               |

## Attachments - Built In Modal

```ts example.ts
import { type Workflow, type WorkflowModalSingleInput } from "@inkeep/uikit";

const exampleWorkflow: Workflow = {
  id: "example_workflow",
  displayName: "Share Error Log",
  goals: [
    "Collect error log information from the user",
    "Provide guidance based on the error log content"
  ],
  botPersona: "You are a helpful technical support assistant",
  informationToCollect: [
    {
      description: "Error log details",
      required: true,
    },
  ],
  initialReplyMessage: "I'd be happy to help you with your error. Please share your error log using the button below.",
  supportedInputs: [{
    id: "error_log_input",
    type: "MODAL",
    displayName: "Share Error Log",
    contentType: {
      type: "CODE",
      contentInputLabel: "Error Log",
      language: "plaintext",
    },
    workflowModalProps: {
      titleInputLabel: "Error Description",
      modalHelpText: "Please paste your error log and provide a brief description of when this error occurred.",
    },
  } as WorkflowModalSingleInput],
};
```

### WorkflowModalSingleInput

This input type will open a **built-in modal** with a form based on the configuration below to collect the user's information.

| Property           | Type                                                   | Description                                                                                                                     |
| ------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| id                 | `string`                                               | **Required**. Id of the input.                                                                                                  |
| type               | `"MODAL"`                                              | **Required**.                                                                                                                   |
| displayName        | `string`                                               | **Required**. Button label in the UI that opens the modal.                                                                      |
| contentType        | `WorkflowCodeContentType` \| `WorkflowTextContentType` | **Required**. Type of content user is inputting. [Learn more](/customization-guides/product-workflows#workflowcodecontenttype). |
| workflowModalProps | `WorkflowModalProps`                                   | Additional modal configuration. [Learn more](/customization-guides/product-workflows#workflowmodalprops).                       |

### WorkflowModalProps

| Property         | Type           | Description                                       |
| ---------------- | -------------- | ------------------------------------------------- |
| titleInputLabel  | `string`       | Label for the title input. Defaults to `"Title"`. |
| modalHelpText    | `string`       | Help text shown in the modal.                     |
| modalHelpElement | `ReactElement` | Help element shown in the modal.                  |

### WorkflowCodeContentType

| Property          | Type         | Description                                                                                                     |
| ----------------- | ------------ | --------------------------------------------------------------------------------------------------------------- |
| type              | `"CODE"`     | **Required**.                                                                                                   |
| contentInputLabel | `string`     | **Required**. Label for the content input, also provided to the LLM when chat is submitted.                     |
| attachmentIcon    | `CustomIcon` | Icon next to the title in the attachment item. [Learn more](/customization-guides/style-components#customicon). |
| language          | `string`     | Code language.                                                                                                  |

### WorkflowTextContentType

| Property          | Type         | Description                                                                                                     |
| ----------------- | ------------ | --------------------------------------------------------------------------------------------------------------- |
| type              | `"TEXT"`     | **Required**.                                                                                                   |
| contentInputLabel | `string`     | **Required**. Label for the content input, also provided to the LLM when chat is submitted.                     |
| attachmentIcon    | `CustomIcon` | Icon next to the title in the attachment item. [Learn more](/customization-guides/style-components#customicon). |

### MessageAttachment

| Property    | Type                                                   | Description                                                                                  |
| ----------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| contentType | `WorkflowCodeContentType` \| `WorkflowTextContentType` | **Required**. [Learn more](/customization-guides/product-workflows#workflowcodecontenttype). |
| title       | `string`                                               | **Required**. Attachment title.                                                              |
| content     | `string`                                               | **Required**. Attachment content.                                                            |
| id          | `string`                                               | **Required**. Attachment id.                                                                 |
| context     | `string[]`                                             | Additional attachment context.                                                               |

## Attachments - In-app callback

```ts example.ts
import { type Workflow, type WorkflowFunctionalMultiInput, type MessageAttachment } from "@inkeep/uikit";

const troubleshootErrorWorkflow: Workflow = {
  id: "troubleshoot_error_workflow",
  displayName: "Troubleshoot Error",
  goals: ["Collect error information", "Provide initial troubleshooting steps"],
  botPersona: "You are an expert technical support engineer",
  informationToCollect: [{ description: "Error details", required: true }],
  initialReplyMessage: "I'm here to help troubleshoot. Let's gather some information about the error.",
  supportedInputs: [{
    id: "fetch_error_data",
    type: "FUNCTIONAL_MULTI_ATTACHMENT",
    displayName: "Fetch Error Info",
    onInvoke: ({ callback, currentMessageAttachments }) => {
      // either call your API or trigger an in-app modal or other UI
      setTimeout(() => {
        const errorLogAttachment: MessageAttachment = {
          id: "error_log_attachment",
          title: "Error Log",
          content: "Error: Unable to connect to database\nTimestamp: 2023-04-15T14:30:00Z",
          contentType: { type: "CODE", contentInputLabel: "Error Log", language: "plaintext" }
        };
        // pass information back to the workflow
        callback([...currentMessageAttachments, errorLogAttachment]);
      }, 1000);
    },
  } as WorkflowFunctionalMultiInput],
};
```

### WorkflowFunctionalMultiInput

This input type allows you to provide your own custom logic to pass information back to the chatbot as an attachment. This custom logic might be your own form, modal, or even via APIs that fetch information about a user from your own downstream services.

| Property    | Type                                | Description                                                                 |
| ----------- | ----------------------------------- | --------------------------------------------------------------------------- |
| id          | `string`                            | **Required**. Id of the input.                                              |
| type        | `"FUNCTIONAL_MULTI_ATTACHMENT"`     | **Required**.                                                               |
| displayName | `string`                            | **Required**. Button label in the UI that triggers the `onInvoke` function. |
| onInvoke    | `(OnInvokeArgs) => void` See below. | **Required**. Function that runs when the button is clicked.                |

#### OnInvokeArgs

`onInvoke` is called with the following arguments:

| Property                  | Type                                                           | Description                                                                                                                                                                                                                                                      |
| ------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| workflow                  | `Workflow`                                                     | The workflow the user has selected.                                                                                                                                                                                                                              |
| selectedInputType         | `WorkflowModalSingleInput` \| `WorkflowFunctionalMultiInput[]` | The input type the user has selected.                                                                                                                                                                                                                            |
| callback                  | `(messageAttachments: MessageAttachment[]) => void`            | Function to update the message attachments. This will override what is currently in state so append any new attachments to `currentMessageAttachments` to avoid removing an attachment. [Learn more](/customization-guides/product-workflows#messageattachment). |
| currentMessageAttachments | `MessageAttachment[]`                                          | The message attachments that user attached. [Learn more](/customization-guides/product-workflows#messageattachment).                                                                                                                                             |


# Sharable chats



Enable the **Share** button in your Inkeep components.

<img className="w-full block dark:hidden" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/sharable-chats/sharable-chats-light.png" />

<img className="w-full hidden dark:block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/sharable-chats/sharable-chats-dark.png" />

When clicked, it generates a link to a chat so a user can share with other users.

<Steps>
  <Step title="Create a dedicated &#x22;View&#x22; page">
    We first need to create a page where the share links will lead to (e.g. `https://inkeep.com/ask-ai`). This should be a
    page in your documentation, app, or marketing site that contains the Inkeep Embedded Chat component.

    Reference:

    *   [React](/ui-components/react/embedded-chat)
    *   [JavaScript](/ui-components/js-snippet/embedded-chat)

    Examples:

    *   [Next.js](/integrations/nextjs/embedded-chat)
    *   [Webflow](/integrations/webflow/embedded-chat)

    The Embedded Chat automatically displays any conversation based on if a `?chatId=` query parameter is present in the URL of the loading the page it's embedded in.
  </Step>

  <Step title="Configure the Inkeep components">
    Set the value of `aiChatSettings.shareChatUrlBasePath` to the URL from the previous step for all Inkeep components you want to enable the **Share** button for. This can include any Search Bar, Chat Button, or Custom Trigger you use in any site or application.

    When clicked, it'll copy a link to the user's clipboard in the format of `https://{shareChatUrlBasePath}?chatId={sharedChatId}`.

    Also set `aiChatSettings.isChatSharingEnabled` to `true`.
  </Step>
</Steps>

## Example

```typescript
import { type InkeepAIChatSettings } from "@inkeep/uikit";

const inkeepAIChatSettings: InkeepAIChatSettings = {
  //... rest of inkeepAIChatSettings
  isChatSharingEnabled: true,
  shareChatUrlBasePath: "https://mydomain.com/ask-ai",
};
```


# Style the components

Customize the search and chat widgets to match your brand.

## The `theme` object

The `theme` object is part of the `baseSettings` and can be used to customize the widgets to fit your brand's style. It allows you to overrides variables that we use throughout our styling.

### Fonts

Specify which fonts should be applied to the body, headings, or code (mono) elements. For example:

```javascript
//...
baseSettings: {
  //...
  theme: {
    tokens: {
      fonts: {
        heading: "'Space Grotesk'",
        body: "'Inter'",
        mono: "'Space Mono', monospace",
      },
    },
  }
}
```

### Font sizes

Customize the font sizes that are used throughout the widget. Below are the default font size tokens.

```javascript
//...
baseSettings: {
  //...
  theme: {
    tokens: {
      fontSizes: {
        '3xs': '0.45rem',
        '2xs': '0.625rem',
        xs: '0.75rem',
        sm: '0.875rem',
        md: '1rem',
        lg: '1.125rem',
        xl: '1.25rem',
        '2xl': '1.5rem',
        '3xl': '1.875rem',
        '4xl': '2.25rem',
        '5xl': '3rem',
        '6xl': '3.75rem',
        '7xl': '4.5rem',
        '8xl': '6rem',
        '9xl': '8rem',
      },
    },
  }
}
```

### Colors

The widget ships with a neutral gray scheme and derives accent colors from the `primaryBrandColor` (passed into `baseSettings`), but these colors can be overridden with your own colors if you wish. Here is an example:

```javascript
//...
baseSettings: {
  //...
  theme: {
    tokens: {
      colors: {
        // grays for light mode
        'gray.50': '#f8f9fa',
        'gray.100': '#f1f3f5',
        'gray.200': '#eceef0',
        'gray.300': '#e6e8eb',
        'gray.400': '#dfe3e6',
        'gray.500': '#BDC2C7',
        'gray.600': '#889096',
        'gray.700': '#687076',
        'gray.800': '#36424A',
        'gray.900': '#11181c',
        // grays for dark mode
        'grayDark.50': '#ededed',
        'grayDark.100': '#a0a0a0',
        'grayDark.200': '#7e7e7e',
        'grayDark.300': '#707070',
        'grayDark.400': '#505050',
        'grayDark.500': '#3e3e3e',
        'grayDark.600': '#343434',
        'grayDark.700': '#2e2e2e',
        'grayDark.800': '#282828',
        'grayDark.900': '#1c1c1c',
      },
    },
    primaryColors: {  // by default, derived from primaryBrandColor
      textColorOnPrimary: '#11181c',
      textBold: '#141d20',
      textSubtle: '#354a51',
      lighter: '#e5feff',
      light: '#85f0ff',
      lightSubtle: '#f1f8fa',
      medium: '#26d6ff', // primaryBrandColor
      strong: '#00b5dd',
      stronger: '#006881',
      hitContentPreview: '#00b5dd',
      hitContentPreviewHover: '#006881',
      textColorOnPrimary: 'white',
    },
  }
}
```

### Z-indices

Customizing the zIndex tokens is usually not necessary but can be helpful if there is an element on your site with a high z-index that is being rendered on top of one of the widgets or modal. Below are the default token values. It is a good idea to update the rest of z-index tokens relative to the one you need to change, for example if changing `modal` to `2000` make sure to also bump `popover` to `2100`, `skipLink` to `2200` etc.

```javascript
//...
baseSettings: {
  //...
  theme: {
    tokens: {
      zIndex: {
        hide: -1,
        auto: 'auto',
        base: 0,
        docked: 10,
        dropdown: 1000,
        sticky: 1100,
        banner: 1200,
        overlay: 1300,
        modal: 1400,
        popover: 1500,
        skipLink: 1600,
        toast: 1700,
        tooltip: 1800,
      },
    }
  }
}
```

### Search Bar

The Search Bar component has two variants to chose from `subtle` and `emphasized`, the default is `subtle`. There are also four size variants to chose from `expand`, `compact`, `shrink`, or `medium`, the default is `compact`. The [Docusaurus package](/integrations/docusaurus) defaults to `shrink`.

<img className="block dark:hidden" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/customization-guides/images/searchbar/searchbar-variants-light.png" />

<img className="hidden dark:block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/searchbar/searchbar-variants-dark.png" />

To change the variant and size:

```javascript
//...
baseSettings: {
  //...
  theme: {
    components: {
      SearchBarTrigger: {
        defaultProps: {
          size: 'expand', // 'expand' 'compact' 'shrink' 'medium'
          variant: 'emphasized', // 'emphasized' 'subtle'
        },
      },
    }
  }
}
```

### Embedded Chat

#### Theme variants

The Embedded Chat component has two styling variants: `container-with-shadow` and `no-shadow`, the default is `container-with-shadow`. There are also four size variants: `shrink-vertically`, `expand`, `default`, or `full-viewport`, the default is `default`.

To change the variant and size:

```javascript
//...
baseSettings: {
  //...
  theme: {
    components: {
      AIChatPageWrapper: {
        defaultProps: {
          size: 'shrink-vertically', // 'shrink-vertically' 'expand', 'default', 'full-viewport'
          variant: 'no-shadow', // 'no-shadow' or 'container-with-shadow'
        },
      },
    }
  }
}
```

#### Centering and sizing

The embedded chat is sized relative to it's container. By default, it expands to a certain max width and heights that preserve a reasonable aspect ratio for different breakpoints and tries to center itself relative to its parent. So it's properly centered, consider wrapping it in parents that have the below stylings.

##### With a `display='flex'` parent

<br />

<CodeGroup>
  ```jsx React
  <div style={{ // parent
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      height: 'calc(100vh - 16px)',
    }}>
    <div style={{ // container
      maxHeight: '600px',
      height: '100%',
     }}>
      <EmbeddedChat {...embeddedChatProps} />
    <div>
  </div>
  ```

  ```html HTML
  <div
    style="display: flex; align-items: center; justify-content: center; height: calc(100vh - 16px);"
  >
    <div style="max-height: 600px; height: 100%;">
      <div id="ikp-embedded-chat-target"></div>
    </div>
  </div>
  ```
</CodeGroup>

<br />

<br />

##### With a `display='block'` parent

<br />

<CodeGroup>
  ```jsx React
  <div style={{ // parent
      display: 'block',
      position: 'relative',
      height: 'calc(100vh - 16px)',
    }}>
    <div style={{ // container
      maxHeight: '600px',
      height: '100%',
      position: 'absolute',
      top: '50%',
      left: '50%',
      transform: 'translate(-50%,-50%)',
    }}>
      <EmbeddedChat {...embeddedChatProps} />
    <div>
  </div>
  ```

  ```html HTML
  <div style="display: block; position: relative; height: calc(100vh - 16px);">
    <div
      style="max-height: 600px; height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"
    >
      <div id="ikp-embedded-chat-target"></div>
    </div>
  </div>
  ```
</CodeGroup>

<br />

<br />

##### Customizing dimensions (using `size='expand'`)

<br />

<CodeGroup>
  ```jsx React
  <div style={{ // parent
      minHeight: '600px',
      height: 'calc(100vh - 16px)',
    }}>
    <div style={{ // container
      maxWidth: '750px',
      maxHeight: '600px',
      height: '100%',
      width: '100%',
      minWidth: '0px',
     }}>
      <EmbeddedChat {...embeddedChatProps} />
    <div>
  </div>
  ```

  ```html HTML
  <div style="min-height: 600px; height: calc(100vh - 16px);">
    <div
      style="max-width: 750px; max-height: 600px; height: 100%; width: 100%; min-width: 0px;"
    >
      <div id="ikp-embedded-chat-target"></div>
    </div>
  </div>
  ```
</CodeGroup>

<br />

<Tip>
  We recommend setting the height for the container div to `height: min(80vh,
    800px);`
</Tip>

## Syntax highlighting theme

By default, the Prism syntax highlighting theme we use is [oneLight](https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/prism-react-renderer/src/themes/oneLight.ts) in light mode and [vsDark](https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/prism-react-renderer/src/themes/vsDark.ts) in dark mode. To use a different theme, you can provide it in the `theme.syntaxHighlighter` prop.

For example, to use the `dracula` theme:

```javascript
import { themes } from "prism-react-renderer";

const inkeepProps = {
  theme: {
    syntaxHighlighter: {
      lightTheme: themes.dracula, // theme used in light mode
      darkTheme: themes.dracula, // theme used in dark mode
    },
  },
};
```

You can use published Prism themes or [create your own](https://github.com/PrismJS/prism-themes).

## Dark mode

### Controlling the color mode

By default, the widgets will be rendered in light mode, to change the color mode use the `forcedColorMode` prop in the `colorMode` object of the `baseSettings`.

```javascript
//..
baseSettings: {
  // ...
  colorMode: {
    forcedColorMode: 'dark', // options: 'light' or dark'
  }
}
```

### System color mode

To use the user's system preference color mode, set the `enableSystem` prop to `true` (by default it is `false`) in the `colorMode` object of `baseSettings`.

```javascript
 baseSettings: {
  //...
  colorMode: {
      enableSystem: true,
    }
  }
```

## Customize the icons

<Snippet file="style_components/CustomizeIcons.mdx" />

## Custom CSS and styling

Additional customization can be done via CSS stylesheets. Because the Inkeep widgets are isolated via a shadow DOM, any custom styling must be passed to the widget configuration in order for them to be applied.

## InkeepStyleSettings

| Property       | Type             | Description                                               |
| -------------- | ---------------- | --------------------------------------------------------- |
| stylesheetUrls | `string[]`       | Array of URLs to stylesheets with style overrides.        |
| stylesheets    | `ReactElement[]` | Array of `<link>` or `<style>` tags with style overrides. |

### Via `stylesheetUrls`

Pass the URL(s) of the stylesheet(s). While the URLs can be either relative or absolute, it is recommended to use absolute URLs, such as those from a CDN. If using a relative path please ensure that the file is bundled and published publicly and is accessible on all pages where the widget is rendered.

```javascript
const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  baseSettings: {
    // ... typeof InkeepBaseSettings
    theme: {
      stylesheetUrls: ["cdn.com/path-to-stylesheet.css"],
    },
  },
  //...
};
```

You can also define the styles inline and encode them as a URL, this is particularly useful if you don't want to create a new stylesheet or if you cannot easily add external stylesheets to your project.

```javascript
const inkeepStyleString = ".ikp-floating-button { color:  #000000; }";
const encodedStyles = encodeURIComponent(inkeepStyleString);
const stylesheetLink = `data:text/css;charset=UTF-8,${encodedStyles}`;

const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  baseSettings: {
    // ... typeof InkeepBaseSettings
    theme: {
      stylesheetUrls: [stylesheetLink],
    },
  },
  //...
};
```

### Via `stylesheets`

Alternatively if using React, you may *instead* pass an array of `<link>` or `<style>` tags to the `stylesheets` property. Please ensure that each element has a unique key. For example:

```javascript
const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  baseSettings: {
    // ... typeof InkeepBaseSettings
    theme: {
      stylesheets: [
        <link
          key="my-stylesheet-key"
          rel="stylesheet"
          href="cdn.com/path-to-stylesheet.css"
        />,
      ],
    },
  },
  //...
};
```

You can also inline the styles and pass them to a style tag instead.

```javascript
const styleOverrides = `
  .ikp-floating-button {
    color:  #000000;
  }
`;

// be sure to provide a unique key
const styleTag = <style key="style-tag-1">{styleOverrides}</style>;

const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  baseSettings: {
    // ... typeof InkeepBaseSettings
    theme: {
      stylesheets: [styleTag],
    },
  },
  //...
};
```

### Hosting the stylesheet

<Note>
  Please ensure that stylesheets are in valid, **publicly accessible** URLs and
  correctly load. If a stylesheet does not load, the Inkeep widgets will not
  render.{" "}
</Note>

You can host your stylesheet in a CDN or include it as part of the public assets exposed in an existing application.

For example:

<Tabs>
  <Tab title="Next.js">
    If using [Next.js](https://nextjs.org/docs), you can create a css file within the [public folder](https://nextjs.org/docs/app/building-your-application/optimizing/static-assets) and then pass that url to the `stylesheetUrls` prop. For example:

    ```javascript
    // for this example there is a file in the public folder is called inkeep.css

    const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
      baseSettings: {
        // ... typeof InkeepBaseSettings
        theme: {
          stylesheetUrls: ["/inkeep.css"],
        },
      },
      //...
    };
    ```
  </Tab>

  <Tab title="Gatsby">
    #### Gatsby

    If using [Gatsby](https://www.gatsbyjs.com/), you can create a static folder in the root of your project, then create a css file for the Inkeep widget style overrides, this will then get copied to the [public folder](https://www.gatsbyjs.com/docs/how-to/images-and-media/static-folder/), then you can pass the url to the `stylesheetUrls` prop. For example:

    ```javascript
    // for this example there is a file in the static folder is called inkeep.css

    const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
      baseSettings: {
        // ... typeof InkeepBaseSettings
        theme: {
          stylesheetUrls: ["/inkeep.css"],
        },
      },
      //...
    };
    ```
  </Tab>
</Tabs>

<Snippet file="style_components/InkeepClassNames.mdx" />

### Using Inkeep CSS variables

Inkeep CSS variables can be utilized within stylesheets to customize elements. For example, the Chat Button uses a dark gray color by default. You can use the `.ikp-floating-button` and any of the pre-defined theme color tokens or any color of your choice to customize it:

```css
.ikp-floating-button {
  background: var(--ikp-colors-inkeep-primary-medium);
}
```

### Dark mode style overrides

To apply a specific style for dark mode only, utilize the `[data-theme='dark']` attribute. For example:

```css
[data-theme="dark"] .ikp-floating-button {
  background: #353e52;
}
```


# Add your own analytics

Use our callback function to log events from the search and chat widgets to your own analytics tool.

## Scenario

To understand how usage of the Inkeep search or chat affects your business, you may want to log events from the search and chat widgets to your own analytics tool. This could be Mixpanel, Posthog, Amplitude, Segment or other analytics or CDP tools.

We expose all the events that we log to our own analytics suite through a callback function called `logEventCallback` that can be configured in the [`baseSettings`](/ui-components/common-settings/base) configuration.

Below is an example of how to use `logEventCallback` to log events related to the `chat_message_bot_response_received` event to your analytics tool. You can check out the full reference of events by inspecting the `InkeepCallbackEvent` type in the npm package.

We recommend you only log the events and properties on the events that you find relevant.

## Example

```typescript
// customAnalyticsCallback.ts
import { InkeepCallbackEvent } from '@inkeep/uikit';

const customAnalyticsCallback = (event: InkeepCallbackEvent): void => {
  // Check if the event type is 'chat_message_bot_response_received'
  if (event.eventName === 'chat_message_bot_response_received') {
    const { question, responseMessage } = event.properties;

    // Log to your own analytics tool or CDP.
    console.log('Question: ', question);
    console.log('Response: ', responseMessage.content);
  }
};

export customAnalyticsCallback;
```

## Available events

Accessible via the `eventName` property on the `InkeepCallbackEvent` type.

| Event name                              | Description                                                                   |
| --------------------------------------- | ----------------------------------------------------------------------------- |
| chat\_message\_submitted                | User submits a chat message                                                   |
| chat\_message\_bot\_response\_received  | Bot response to user message is received                                      |
| search\_query\_submitted                | Search query is submitted                                                     |
| search\_query\_response\_received       | Response to search query is received                                          |
| search\_result\_clicked                 | Search result is clicked                                                      |
| chat\_thumbs\_up\_feedback\_submitted   | User submits thumbs up feedback                                               |
| chat\_thumbs\_down\_feedback\_submitted | User submits thumbs down feedback                                             |
| chat\_history\_cleared                  | Chat history is cleared                                                       |
| chat\_share\_button\_clicked            | Share button is clicked                                                       |
| chat\_message\_copied                   | Chat message is copied                                                        |
| chat\_code\_block\_copied               | Code block in chat is copied                                                  |
| chat\_response\_citation\_clicked       | Citation link is clicked                                                      |
| get\_help\_option\_clicked              | Get help item is clicked                                                      |
| support\_button\_clicked                | Support button (if configured as part of the aiAnnotationPolicies) is clicked |

{/* | search_result_clicked | search result is clicked | */}

{/* | search_result_upvote | search result is upvoted | */}

{/* | search_result_downvote | search result is downvoted | */}

{/* | call_to_action_clicked | call to action is clicked | */}

{/* | chat_revised_answer_submitted | user submits revised answer | */}

{/* | chat_response_citation_clicked | citation in response is clicked | */}

{/* | chat_bot_had_insufficient_knowledge | bot had insufficient knowledge to answer | */}

{/* | modal_mode_switched | modal mode is switched | */}

{/* | chat_shared_session_loaded | loading a shared chat session | */}

{/* | chat_shared_session_continued | continuing a shared chat session | */}

{/* | chat_widget_loaded | chat widget finishes loading | */}

{/* | shared_chat_loaded | shared chat is loaded | */}

{/* | modal_opened | modal is opened | */}

{/* | modal_closed | modal is closed | */}

## Common properties

The below are shared by all events.

| Property name        | Description                                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
| organization\_id     | Organization ID                                                                                                  |
| widgetLibraryVersion | Widget library version                                                                                           |
| interactionType      | Type of interaction. Value can be one of `'EMBEDDED_CHAT' \| 'CHAT_BUTTON' \| 'CUSTOM_TRIGGER' \| 'SEARCH_BAR'`. |

{/* | product                | Product associated with interaction, if provided to the React component.                                         | */}

{/* | is_modal_mode_switching_enabled | Whether modal mode switching is enabled                                                                          | */}

{/* | chat_mode_component_default     | Default chat mode                                                                                                | */}

{/* | is_chat_mode_toggle_enabled     | Whether chat mode toggle is enabled                                                                              | */}

{/* | shared_chat_session_id          | ID of shared chat session                                                                                        | */}

{/* | integration_id         | Integration ID                                                                                                   | */}

### User props

If provided to the React component, the below user properties are available.

| Property name          | Description                                                                                                                |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| userIdentificationType | How user is identified. Can be of values `'ANONYMOUS' \| 'COOKIED' \| 'ID_PROVIDED'` depending on analytics configuration. |
| userCohorts            | User cohorts                                                                                                               |
| userEmail              | User email                                                                                                                 |

{/* | user_type                | Type of user           | */}

### Chat properties

The below are shared by all chat events.

| Property name             | Description                  |
| ------------------------- | ---------------------------- |
| chatModeCurrentlySelected | Currently selected chat mode |
| chatSessionId             | ID of shared chat session    |


# Comparisons

How does Inkeep compare to other AI search and chat services?

## TL;DR

Teams that have evaluated our chat quality with other products have generally favored Inkeep because our AI:

*   admits when it doesn't know and guides users to support channels
*   contains minimal (if any) hallucinations or search misses
*   provides rich citations that help users inspect answers
*   leverages many sources while prioritizing authoritative content

That said - don't take our word for it! Use our [free trial](https://inkeep.com/demo-form) to test out Inkeep with your toughest questions.

## Comparisons

<Accordion title="How does Inkeep compare to DIY?">
  Teams typically look for a managed solution so that they don't have to invest engineering resources in:

  *   Building the pipelines and infra to ingest and keep content up to date
  *   Experimenting with LLM models and RAG techniques to get high-quality results
  *   Building user-friendly chat UIs that are embeddable anywhere
  *   Providing insights to product and documentation teams
</Accordion>

<Accordion title="How does Inkeep compare to Algolia?">
  Algolia is a popular service that is particularly focused on just search. While Algolia offers "Neural" or "AI search", those terms refer to search algorithms rather than "AI chat" or conversational experiences.

  While Inkeep similarly provides a neural search service (with unlimited usage in any plan), we also provide:

  *   AI chat experiences powered by large language models and your own content
  *   An analytics platform to analyze user conversations - not just search queries.
  *   Slack and Discord bots that can be used in your employee or customer-facing channels
  *   Zero-effort ingestion of any website using our intelligent scraper (no need for managing content over APIs!)

  Many teams use Inkeep for just the chat experiences - you don't have to replace your existing Algolia search experience if you're happy with it.
</Accordion>

<Accordion title="How does Inkeep compare to Intercom?">
  Intercom is a general support solution for any type of product - consumer-facing, B2B SaaS products, etc. Their offering includes "Fin", an AI chatbot focused on support deflection.

  In comparison to Intercom, Inkeep shines for:

  *   UX experiences tailored for developer-facing or technical products
  *   Being easy to embed in any touch point - Slack, Discord, Search Bar, Chat button, etc.
  *   Zero-effort ingestion of any content source
  *   A focus on feedback loops for technical documentation teams
  *   Affordable pricing
</Accordion>

<Accordion title="How does Inkeep compare to Kapa.ai?">
  Kapa.ai is a startup focused on AI chat for developer products.

  While Inkeep and Kapa.ai share similarities, Inkeep includes:

  *   AI generated reports that provide actionable summaries of documentation gaps
  *   Inline citations (as footnotes \[1]) in chat answers
  *   A [Search Bar](/ui-components/react/search-bar) component for rich search experiences
  *   An [Embedded chat](/ui-components/react/embedded-chat) component for help centers
  *   Built-in experience for [creating support tickets](/customization-guides/embedded-support-form) with your support system
  *   UI components that are highly configurable and themable, support dark and light modes, and are available as both React components as well as JavaScript snippets for integration in any.
  *   A fully conversational copilot for support teams available in any support platform.
</Accordion>

<Accordion title="How does Inkeep compare to Mendable?">
  Mendable is a startup focused on AI chat for sales and support.

  While Inkeep and Mendable share similarities, Inkeep includes:

  *   AI generated reports that provide precise summaries of documentation gaps
  *   Inline citations (as footnotes \[1]) in chat answers
  *   Built-in experience for [creating support tickets](/customization-guides/embedded-support-form) with your support system
</Accordion>

<Accordion title="How does Inkeep compare to OpenAI Assistants?">
  OpenAI's Assistant's API helps companies build "agentic" workflows, where an assistant can use a variety of tools and OpenAI maintains chat history and state of usage of those tools. One of those tools is "retrieval", which can perform RAG on pre-uploaded documents. It's up to the user to programmatically fetch those documents, format them, and upload them and build out a UI and their own logic.

  Inkeep's AI chat service, by comparison, is meant to be an end-to-end offering that requires zero engineering effort while providing high-quality results. Beyond abstracting away retrieval, Inkeep's service also provides:

  *   Out-of-box connectors for ingesting and syncing content from Websites, Slack, GitHub, etc.
  *   UI components that are easily embeddable in any website
  *   A search API and UI components that can replace traditional search services like Algolia
  *   Analytics for your team on documentation gaps and trending questions
  *   High availability and resiliency against outages with one LLM provider
</Accordion>

<Accordion title="How does Inkeep compare to SiteGPT or Chatbase?">
  SiteGPT and Chatbase are customer support AI chatbots tailored for traditional consumer or B2B SaaS solutions.

  While Inkeep shares some similarities, customers who've compared or migrated from these platforms have generally found Inkeep to have answers with significantly less hallucinations and better RAG performance. Inkeep is also more geared for technical B2B SaaS or developer tool products.
</Accordion>

<Accordion title="How does Inkeep compare in pricing to other services?">
  We aim to make our service accessible to companies of all sizes and needs while providing best-in-class responses and quality.

  **Inkeep offers unlimited questions** in any plan. For comparisons to Inkeep's pricing, evaluate estimated usage against a company's pricing for state-of-the-art models like OpenAI GPT-4o or Claude Sonnet 3.5 models and not lighter-weight models like OpenAI GPT-3.5 or Claude Haiku.

  We suggest teams evaluate solutions based for high quality responses and experiences that match your user's expectations.
</Accordion>


# Features



Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).

<Accordion title="Does Inkeep support non-English languages?">
  Yes, our AI assistant should automatically answer in the language that the
  user used. You can also set a 'primary language' in your project settings if
  your users or documentation are primarily in a different language. This will
  bias our AI and content ingestion service to account for that language (but
  still work for any). You can also customize the strings or use an
  internationalization solution with Inkeep's UI components
</Accordion>

<Accordion title="Is Inkeep available as an OpenAI compliant chat completion endpoint?">
  Yes. This feature is currently in private preview, please reach out for access. Using this feature allows you to use Inkeep with any library or SDK that is OpenAI compatible.
</Accordion>

<Accordion title="Can I self host Inkeep?">
  Currently Inkeep is not setup to be a self-hostable solution. However, if your concern is data processing or privacy concerns, review our [privacy overview](https://docs.inkeep.com/overview/privacy) to learn about options available for customers with custom data processing requirements.
</Accordion>

<Accordion title="Is Inkeep Open Source?">
  While we love supporting open source teams and projects like Drizzle, Auth.js and others, our solution is not currently open source.
</Accordion>

<Accordion title="Does Inkeep ingest or show images?">
  For security reasons, our UI components don't render images or content from URLs. We typically don't ingest images out of the box, but let us know if images are an important part of your documentation. We are able to ingest them if needed.
</Accordion>

<Accordion title="Does Inkeep ingest YouTube videos or video content?">
  Because YouTube transcriptions are not high quality and often only make sense in relation to the graphics in the videos, we generally don't recommend using raw videos as a source. However, let us know if this is important for you, we have done it in the past for some customers.
</Accordion>


# Projects & Sources



Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).

<Accordion title="What is a 'source'?">
  A source is a collection of content, often deliminated by sitemaps, url paths, or other logical units. For example, a GitHub repository is "1" source. A marketing site is another. A documentation site can be one or split into multiple. We consider a source seperate if it's crawled independently of others.
</Accordion>

<Accordion title="When should I create a separate project?">
  Projects are often used to represent different core scenarios or sets of sources. For example, teams sometimes create different projects for different product lines or to separate out internal (employee) facing scenarios that use private content sources compared to a customer-facing project with public sources.
</Accordion>

<Accordion title="When should I create an integration?">
  Integrations represent different "instances" or locations of a chatbot for your project. For example, you may register one integration for your marketing site, one for your documentation site, and another for your help desk. Our analytics views are filterable by integration - allowing you to segment usage patterns. Each integration gets its own API key. All project settings and sources assigned to that project apply to an integration.
</Accordion>

<Accordion title="Can chat or search API keys be included in public clients?">
  While it is up to your company's own policies and best practices, chat and search API keys are generally ok to be included in client-side (browser) code if the search and chat service is intended for public, unauthenticated use. This is typical of public-facing documentation sites or marketing pages. We implement various protection mechanisms to prevent against abusive usage and in general the chat service is scoped to answering questions about your product so is not a general use API key like an OpenAI or general LLM provider API key would be. If you would like to implement your own protection mechanisms, it is possible to have traffic routed through a private proxy, implement CAPTCHA, and/or add user authentication.
</Accordion>

<Accordion title="How do I use Inkeep in development or staging environments?">
  Teams typically create an Integration within a project they use for staging environments. This can help separate out usage from your production usage. If you need truly independent staging or testing environments, you can set up two projects that mirror each other instead.

  Separately, if using our UI components, you can set `baseSettings`.`env` to `DEVELOPMENT` so that questions asked in that mode are not shown in the analytics dashboard.
</Accordion>


# Pricing



Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).

<Accordion title="How quickly will I receive a sandbox?">
  We aim to send a sandbox in under an hour for most small to medium websites. If a site is very large or we detect that it needs human review, it may take more than an hour. We try to share the demo as quickly as possible while maintaining quality, so please [reach out](mailto:help@inkeep.com) if you have any urgent questions or don't hear back from us within a day.
</Accordion>

<Accordion title="Is there a usage limit?">
  We aim to account for your expected usage in the pricing we quote you. If you're using a self-serve plan and your usage exceeds typical usage patterns, we'll communicate with your team to find pricing that makes sense for your scenario. Note that we generally We monitor usage patterns to ensure fair use and maintain service quality for all customers. If we notice unusually high or potentially abusive usage, we may reach out to discuss your needs and explore solutions.
</Accordion>

<Accordion title="Does Inkeep offer a free trial or have a free tier?">
  To get a free demo on your content, just fill out this form [here](https://inkeep.com/demo-form?cta_id=nav_demo_cta). You'll get a personalized sandbox over email, or you can schedule a call with our team for a full product demo. A sandbox is just a sharable link where you can try our UI components and chat service against your own content without creating an account or providing a payment method.

  For self-serve customers, we offer a 30-day free trial. You can use that time to use our Slack bot in your internal support channels, launch publicly, or otherwise test Inkeep to your liking.

  If you have custom or complex requirements or would like direct ongoing support from our team, we generally offer a 30 day pilot at a flat price with unlimited usage. We work with you during this period to integrate Inkeep wherever you'd like to try it, measure usage, and work through any implementation details or requirements that come up.
</Accordion>

<Accordion title="What is AI or neural search and how is it different than AI chat?">
  A "search" refers to a traditional search query -- a user enters a few words or a phrase, and Inkeep returns a list of relevant documents. Our search service is available if you (optionally) use our Search Bar component or via our API. Unlimited searches are included in any plan.

  Our AI search is powered by proprietary neural net and lexical search algorithms that deliver fast, highly relevant out of the box search results without any tuning. Our AI search is part of our "RAG" system we use to feed relevant content to to large language models to power our "Ask AI" chat experience.
</Accordion>

<Accordion title="How does Inkeep protect against spam or bot usage?">
  We use a variety of mechanisms to protect our service against malicious or miscellaneous usage. For example, we set dynamic IP-level or organization-level throttling to prevent against volume-based attacks. Further, we smartly detect if there is a surge of questions that seem out of the ordinary from your company's typically usage patterns. We strive to mitigate against these attack vectors and have not yet had downtime or cost issues related to malicious usage. If you're particularly keen on enforcing your protection mechanisms, we can work with you to [add authentication](/customization-guides/authenticate-users), add a CAPTCHA service, send usage through your own proxy, or in general find a solution that meets your requirements.
</Accordion>

<Accordion title="Is it possible to remove the Powered by Inkeep branding?">
  By default, the terms of all of our plans ask that customers keep the Inkeep branding in our UI components. If you require a white labeled solution, it can be offered as an add-on to enterprise plans.
</Accordion>

<Accordion title="Is there discount pricing available for Open Source projects?">
  For Open Source projects that don't have a monetized backing entity, we typically do offer free usage. This can be as a Discord bot, Slack bot, or UI components for documentation. Please reach out to our team for details.
</Accordion>

<Accordion title="Can I use Inkeep as a white labeled solution to offer as part of my own product?">
  We do not currently support multi-tenant or reseller/vendor scenarios.
</Accordion>


# Troubleshooting



Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).

<Accordion title="How do I troubleshoot if my widget isn't loading?">
  If you're having trouble loading the Inkeep UI components, try checking the below.

  <Steps>
    <Step title="Check if the widget is loaded in a Shadow DOM">
      Inkeep components are loaded into a "Shadow" DOM. This is done so that they don't affect your pages styles or vice versa. Use your browser's developer tools to check if there's an HTML element with an ID that contains `inkeep-shadow`. If you don't see an element that matches that description, that means there is an issue instantiating the component. This is typically due to an issue with loading the JavaScript.

      If you're using the `uikit-js` package, there are two `scripts` that need to be loaded:

      1.  The component library: `<script id="inkeep-script" type="module" src="https://unpkg.com/@inkeep/uikit-js@<someversion>/dist/embed.js"></script>`
      2.  A script that includes `Inkeep.embed({...})` to instantiate the UI components.

      Inspect your DOM to see if both of these scripts were successfully loaded. Sometimes, the issue is that script #2 loads before script #1. To prevent this, you can add an id to script #1 and use the "load" event to execute script #2.

      ```
      inkeepScript.addEventListener("load", function () {
        // embed logic
      });
      ```
    </Step>

    <Step title="Check if there are invalid custom CSS stylesheets">
      The UI components are designed to not display until custom stylesheets have been fetched. If you specify a `stylesheetUrls` property in the widget configurations but the provided path is invalid, this can cause the widget to not render. Please ensure that the URL is an absolute URL (like for a CDN); or, if using a relative URL, that that relative file is indeed bundled and published publicly. This typically requires putting the CSS file in a `public` or `static` folder, depending on the framework.
    </Step>

    <Step title="Check that the widgets are not hidden by other components on the page">
      If you see the `inkeep-shadow` element, the issue might be that the Search Bar or Chat Button components are not being properly displayed in your page. Find all the elements with id `inkeep-widget-root` and inspect whether they are being rendered off the page or hidden by other elements.
    </Step>
  </Steps>
</Accordion>

<Accordion title="The search bar is unintentionally opening in chat mode?">
  By default, UI components use same-domain cookies in the browser to keep track of what "mode" a user last had open so that they are taken to that same mode when they next open the Inkeep Modal.

  To disable this "remember" behavior, you can set `baseSettings.optOutFunctionalCookies` = `true` or use the `forceInitialDefaultView` prop in `modalSettings` which will open the modal in the `defaultView` regardless of which view was last opened.

  New users will see the default search or chat experience based on your component type or if you customize `modalSettings.defaultView`.
</Accordion>

<Accordion title="Do Inkeep widgets affect page load times?">
  The Inkeep UI components and related scripts are loaded asynchronously so they
  don't affect page performance.
</Accordion>


# Add AI Chat to your Astro app



<Snippet file="astro/QuickStartAstro.mdx" />

<Snippet file="astro/InkeepWidgetSettingsAstro.mdx" />

### Define the component

Next, create an `InkeepChatButton.tsx` file for the [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.

<Snippet file="InkeepChatButtonDefineReact.mdx" />

### Use the component

Now use the `InkeepChatButton.tsx` component in your Astro based website.

```jsx
// ...
<InkeepChatButton client:load />
// ...
```

<Note>
  Use the `client:load` directive to load and hydrate the `InkeepChatButton.tsx` component immediately on page load.
</Note>

<Snippet file="learn_more/ChatButtonSettingsReact.mdx" />


# Add AI search to your Astro app



<Snippet file="astro/QuickStartAstro.mdx" />

<Snippet file="astro/InkeepWidgetSettingsAstro.mdx" />

### Define the component

Next, create an `InkeepSearchBar.tsx` file for the [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.

<Snippet file="InkeepSearchBarDefineReact.mdx" />

### Use the component

Now use the `InkeepSearchBar.tsx` component in your Astro based website.

```jsx
// ...
<InkeepSearchBar client:load />
// ...
```

<Note>
  Use the `client:load` directive to load and hydrate the `InkeepSearchBar.tsx` component immediately on page load.
</Note>

<Snippet file="learn_more/SearchBarSettingsReact.mdx" />


# Add AI Chat to your Bettermode community



## Overview

You can add AI chat as a [Bettermode](https://bettermode.com/) widget to your Bettermode community.

<Snippet file="InkeepGetApiKey.mdx" />

## Create App

To use the AI chat, first we need to create an application on the Bettermode platform.

1.  Open the Bettermode [Developer Portal](https://developers.bettermode.com/portal)
2.  Click on **Create a new app**
3.  Specify a name for the integration, example: **Inkeep Widget**
4.  Select your community from the drop-down menu below
5.  Click on **Create app**

Result:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/bettermode/bettermode-create-app.png" />{" "}

## Customizing App

Next, you need to go to the app you created and customize the widget.

### Initialize the widget

Click on the **Custom code** section and add the following script to the `Custom code for <head>` area:

```html
<script
  id="inkeep-script"
  src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
  type="module"
  defer
></script>

<script defer>
  const baseConfig = {
    apiKey: "INKEEP_API_KEY", // required
    integrationId: "INKEEP_INTEGRATION_ID", // required
    organizationId: "INKEEP_ORGANIZATION_ID", // required
    primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
    organizationDisplayName: "Inkeep",
    // ...optional settings
  };
  const inkeepScript = document.getElementById("inkeep-script");

  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      // optional -- for syncing UI color mode
      colorModeSync: {
        observedElement: document.body,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-scheme-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-scheme-mode",
      },
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          ...baseConfig,
        },
        modalSettings: {
          // optional 
        },
        searchSettings: {
          // optional 
        },
        aiChatSettings: {
          // optional 
          botAvatarSrcUrl: "https://mydomain.com/mylogo",
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) {
      addInkeepWidget();
    }
  });
</script>
```

Click on the **Test and publish** section and click **Publish**.

## Install App

Open community settings, then open the **Apps** section. Select the app you created.

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/bettermode/bettermode-install-app.png" />

Go back to the main community page and reload the page.


# Add an AI chatbot to your Discord server



## Overview

You can add Inkeep as a [Discord](https://discord.com/)  app to your Discord community or team's internal support, solutions engineering, or other channels.

## Get Server ID

To configure the bot, you'll need your Discord Server ID:

1.  Open Discord in your [browser](https://discord.com/app).
2.  Select the target server in the left navigation bar
3.  Copy the `{SERVER_ID}` from the browser's navigation bar. The URL will be in the format of `https://discord.com/channels/{SERVER_ID}/{CHANNEL_ID}`
4.  Alternatively, you can find the ID in server settings.

## Create Integration

To use the Inkeep Discord bot, we first need to register it as an integration.

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Click on **Create Integration**
4.  From the dropdown menu, choose **Discord**
5.  Fill in the required fields **Name** and **Server ID**.
6.  (Optional) If you'd like the bot to *only* reply to tags in a specific channel, specify the channel ID in **Only reply in selected Channel IDs** under **Advanced Settings**. By default, the bot is able to be tagged in any public channel or private channels it is added to.
7.  Click on **Create**

## Add to Server

To install the Inkeep Discord bot in your server:

1.  Click [here](https://install.inkeep.com/ask-discord)
2.  Select the desired server in the **ADD TO SERVER** dropdown
3.  Review the permissions and click **Continue**

<Note> The Discord bot can be added to any type of channel, including forums or normal chat-style channels. For private channels, ensure that the bot is added as a member of the channel. </Note>

To start using the bot, tag **@Ask Inkeep** in a message with your question. You or your users can do this in any channel.

## Create a dedicated channel (optional)

We recommend having a dedicated channel for team or community members to ask questions to the bot.

1.  Create an `✨ask-ai` or similar channel.
2.  Ask an example question in the channel to demonstrate how to use the bot. Type **@Ask Inkeep** before your question and hit enter.
3.  The Inkeep bot will automatically create a new thread (if [enabled](https://support.discord.com/hc/en-us/articles/4403205878423-Threads-FAQ#h_01FBM7FY3D1J3WC3TP71EC7JRF)) and reply to the user.
4.  For follow-up questions, the user **must** tag the bot again.

Since Discord does not have the ability to pin a channel, consider:

*   Making an announcement in your `#general` or other relevant channels linking users to it
*   Adding guidance on how to use the bot in a pinned message on your main channel or incorporate as part of your onboarding experience.

## Customize the Display Name

Instead of using **@Ask Inkeep**, you can customize the bot name from Discord.

1.  Have at least one message that tags **@Ask Inkeep**
2.  In that message, *right* click on **@Ask Inkeep**
3.  Select **Change Nickname**
4.  Enter your desired name for the bot, e.g. `Ask CompanyAI`

The bot can now be tagged via **@Ask CompanyAI** and all existing instances of **@Ask Inkeep** will be updated.

<Accordion title="Can I customize the bot avatar/icon?">
  Unfortunately, Discord does not currently support custom avatars or icons for published bots.
</Accordion>

## Tag a team member

Sometimes, you want users to be able to escalate to a human for help if the bot is not able to help.

To make this flow seamless, you can configure the Discord integration to show **Mark as resolved ✅** and **Ask for help 👋** buttons instead of the default 👍 👎.

When a user clicks on **Ask for help 👋**, the bot can tag users, roles, or other bots.

To set up:

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Select the Discord Integration
4.  Expand **Advanced Settings**
5.  Under **When a user leaves negative feedback...**, click the dropdown menu
6.  Select **Tag a team member**
7.  Specify the **User IDs**, **Role IDs**, or **Bot IDs** you'd like to tag

You can get the ID of a Bot or User by right-clicking on a profile on Discord and clicking on **Copy User ID** or **Copy group ID**.

For a Role, you'll need to:

1.  Navigate to the server
2.  On the top left of the Discord app, click on your server name
3.  Click on **Server Settings**
4.  Navigate to **Roles** from the left side pane
5.  Click on **...** next to the desired role and select **Copy Role ID**

## Auto-reply mode

Instead of requiring users to tag the bot with `@Ask Inkeep`, you can configure the Discord bot to automatically reply to all new threads in specific channels. This can be useful for dedicated support channels or community Q\&A forums.

To set up auto-reply mode:

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Select the Discord Integration
4.  Expand **Advanced Settings**
5.  In the **Auto-reply Channel IDs** field, add the channel IDs where you want the bot to auto-reply to new threads
6.  (Optional) Customize the bot's behavior with these settings:
    *   **Reply message**: Enter a custom initial message for the bot to use when it's tagged or auto-replies
    *   **Is AI Draft Mode enabled?**: Adjusts the bot's tone for generating draft responses for team review
    *   **Is human reviewing conversations?**: Informs users that team members are monitoring conversations
7.  Click **Save** to apply the changes

<Note>
  We generally don't recommend enabling auto-reply for all channels, as it's beneficial for users to get accustomed to tagging the bot, especially for follow-up questions.
</Note>

### Limiting bot responses to specific channels

If you want to restrict the bot to only respond in certain channels:

1.  In the **Advanced Settings**, find the **Only reply in selected Channel IDs** field
2.  Add the channel IDs where you want the bot to be active
3.  The bot will now only respond when tagged in these specified channels

This can be useful for managing the bot's presence in large servers or for creating dedicated AI assistance channels.

<Accordion title="Can I trigger other automations based on `Ask for help👋`">
  We recommend creating a custom bot, e.g. `@Triaging Bot`, that contains the logic you'd like to use for triggering custom down-stream workflows.
</Accordion>

<Tip>Need to make Discord SEO-friendly? Try [Answer Overflow](https://www.answeroverflow.com?utm=inkeep) ↗️ (partner).</Tip>


# Add AI Chat to your Discourse community



## Overview

You can add AI chat as a [Discourse](https://www.discourse.org/) widget to your Discourse community.

<Snippet file="InkeepGetApiKey.mdx" />

## Create Theme-Component

To use the AI chat, first we need to create a theme-component on the Discourse platform.

1.  Click the **Admin** section in the sidebar menu
2.  Open the **Customize** tab
3.  Open the **Themes** subsection
4.  Now, select the **Components** tab
5.  Click on **Install**
6.  Open the **Create new** menu and specify a name for the component
7.  Click on **Create**

Example:

<img className="w-full block " src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/discourse/discourse-create-theme-component.png" />

## Customizing Theme-Component

Next, in the created theme component menu, click **Edit CSS/HTML** to customize the widget.

### Initialize the widget

Click on the **Head** section and paste the following scripts:

<CodeGroup>
  ```html Base Theme
  <script
    src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
    type="module"
    defer
  ></script>

  <script defer>
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional
        },
        searchSettings: {
          // optional
        },
        aiChatSettings: {
          // optional
          botAvatarSrcUrl: "https://mydomain.com/mylogo",
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  </script>
  ```

  ```html Material Design Theme
  <script
    src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
    type="module"
    defer
  ></script>

  <script defer>
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional
        },
        searchSettings: {
          // optional
        },
        aiChatSettings: {
          // optional
          botAvatarSrcUrl: "https://mydomain.com/mylogo",
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });

    /* Below shifts the 'Ask AI' button upwards in mobile so that it doesn't conflict with the Material UI */

    const changeInkeepWidgetPosition = (bottom, widgetRoot) => {
      const styleDiv = document.createElement("style");
      styleDiv.textContent = `.ikp-btn { bottom: ${bottom} !important; }`;
      widgetRoot.appendChild(styleDiv);
    };

    const mutateInkeepWidget = () => {
      let isPluginCreateTopicButton = false;
      const ikpShadowDom =
        document.getElementById("ikp-chat-button").childNodes[0];

      const createTopicButton = document.getElementById("create-topic");
      isCreateTopicButtonVisible =
          createTopicButton && window.getComputedStyle(createTopicButton, null).overflow == "hidden";

      if (ikpShadowDom) {
        const inkeepWidgetRoot =
          ikpShadowDom.shadowRoot.getElementById("inkeep-widget-root");
        isCreateTopicButtonVisible
          ? changeInkeepWidgetPosition("7.5rem", inkeepWidgetRoot)
          : changeInkeepWidgetPosition("1.5rem", inkeepWidgetRoot);
      }
    };

    const observer = new MutationObserver(mutateInkeepWidget);

    observer.observe(document.body, {
      attributes: true,
      childList: true,
      subtree: false,
    });

  </script>
  ```
</CodeGroup>

<Note>Optional: click **preview** to test out the widget.</Note>

Click on the **Save**.

## Apply to themes

In the **Include component on these themes** setting, specify the themes in which the widget will be used.
Alternatively, you can create and specify your own theme.

## Allow list from Content Security Policy (CSP)

If you are using a Discourse version earlier than `3.3.0.beta1` you may need to allow-list the Inkeep script by modifying the Content Security Policy (CSP). In version `3.3.0.beta1` or later external scripts should work without additional configuration, see [here](https://meta.discourse.org/t/mitigate-xss-attacks-with-content-security-policy/104243#csp-and-third-party-integrations-8) for more details:

1.  Click the **Admin** section in the sidebar menu
2.  Open the **Settings** tab
3.  Open the **Security** section
4.  Add `https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js` to the **content security policy script src** setting
5.  Enable **content security policy strict dynamic** setting

Example:

<img className="w-full block " src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/discourse/discourse-enable-csp.png" />

Go back to the main community page and reload the page.


# Add AI Chat to your Document360 docs



<Snippet file="document360/QuickStartDocument360.mdx" />

## Initialize the widget

To integrate the Inkeep chat button into your Document360 documentation, follow these steps:

1.  Navigate to **Custom CSS / Javascript** settings.
2.  Click on the **Javascript** tab.
3.  Add the following script to load the chat button:

```js
document.addEventListener("DOMContentLoaded", () => {
   // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    addInkeepWidget();
  });
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search & Chat Button to your Document360 docs



<Snippet file="document360/QuickStartDocument360.mdx" />

## Initialize the widget

To integrate the Inkeep chat button and search bar into your Document360 documentation, follow these steps:

1.  On the **Home page** remove the default search bar from the **Hero** section
2.  Go to the page where you want to display the widget and create new **Custom code section** with the following html:

```html
<div id="inkeepSearchBar"></div>
```

3.  Navigate to **Custom CSS / Javascript** settings.
4.  Click on the **Javascript** tab.
5.  Add the following script to load the inkeep chat button and search bar:

```js
document.addEventListener("DOMContentLoaded", () => {
  // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Function for customizing the widget configuration
  const inkeepConfig = (componentType, targetElementId) => ({
    componentType,
    targetElement: targetElementId,
    properties: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
          // ...optional settings
        },
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  });

  const addSearchbarWidget = () => {
    Inkeep().embed(inkeepConfig("SearchBar", "#inkeepSearchBar"));

    const observer = new MutationObserver((mutationsList, observer) => {
      const inkeepPortals = document.getElementsByTagName("inkeep-portal");
      
      Array.from(inkeepPortals)?.forEach((inkeepPortal) => {
        inkeepPortal.remove();
      });

      inkeepConfig("SearchBar", "#inkeepSearchBar").targetElement = search;
      Inkeep().embed(inkeepConfig("SearchBar", "#inkeepSearchBar"));
    });

    observer.observe(document.head, { childList: true });
  };

  inkeepScript.addEventListener("load", () => {
    addSearchbarWidget();
    Inkeep().embed(inkeepConfig("ChatButton"));
  });
});
```

<Snippet file="learn_more/CommonSettings.mdx" />


# Add AI Search to your Document360 docs



<Snippet file="document360/QuickStartDocument360.mdx" />

## Initialize the widget

To integrate the Inkeep search bar into your Document360 documentation, follow these steps:

1.  On the **Home page** remove the default search bar from the **Hero** section
2.  Go to the page where you want to display the widget and create a new **Custom code section** with the following html code:

```html
<div id="inkeepSearchBar"></div>
```

3.  Navigate to **Custom CSS / Javascript** settings.
4.  Click on the **Javascript** tab.
5.  Add the following script to disable default search bar and load the inkeep one:

```js
document.addEventListener("DOMContentLoaded", () => {
  // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Function for customizing the widget configuration
  const inkeepConfig = (targetElementId) => ({
    componentType: "SearchBar",
    targetElement: targetElementId,
    properties: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
          // ...optional settings
        },
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  });

  const addInkeepWidget = () => {
    Inkeep().embed(inkeepConfig("#inkeepSearchBar"));

    const observer = new MutationObserver((mutationsList, observer) => {
      const inkeepPortals = document.getElementsByTagName("inkeep-portal");
      
      Array.from(inkeepPortals)?.forEach((inkeepPortal) => {
        inkeepPortal.remove();
      });

      inkeepConfig("#inkeepSearchBar").targetElement = search;
      
      Inkeep().embed(inkeepConfig("#inkeepSearchBar"));
    });

    observer.observe(document.head, { childList: true });
  };

  inkeepScript.addEventListener("load", addInkeepWidget);
});
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Docusaurus docs



## What is Docusaurus

[Docusaurus](https://docusaurus.io/) is an open-source documentation platform powered by MDX and React.

<Snippet file="InkeepGetApiKey.mdx" />

<Snippet file="docusaurus/InkeepQuickStart.mdx" />

## Define the widget

Аdd the chat button as a theme in your `docusaurus.config.js` file:

```js docusaurus.config.js
themes: ["@inkeep/docusaurus/chatButton"],
```

### Сonfiguration settings

Next, configure the widget in the `themeConfig` property:

```js docusaurus.config.js
  //..
  themeConfig: {
    inkeepConfig: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // required -- your brand color, the widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
          syntaxHighlighter: {
            lightTheme: lightCodeTheme, // optional -- pass in the Prism theme you're using
            darkTheme: darkCodeTheme, // optional -- pass in the Prism theme you're using
          },
        }
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // optional -- use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  },
```

<Snippet file="learn_more/CommonSettings.mdx" />

<Snippet file="docusaurus/DocusaurusFAQ.mdx" />


# Add AI Search & Chat to your Docusaurus docs



## What is Docusaurus

[Docusaurus](https://docusaurus.io/) is an open-source documentation platform powered by MDX and React.

<Snippet file="InkeepGetApiKey.mdx" />

<Snippet file="docusaurus/InkeepQuickStart.mdx" />

## Configure the widget

Аdd the Inkeep widgets as themes in the `docusaurus.config.js` file:

```js docusaurus.config.js
themes: ["@inkeep/docusaurus/chatButton", "@inkeep/docusaurus/searchBar"],
```

### Сonfiguration settings

Next, configure the widgets in the `themeConfig` property:

```js docusaurus.config.js
  //..
  themeConfig: {
    inkeepConfig: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // required -- your brand color, the widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
          syntaxHighlighter: {
            lightTheme: lightCodeTheme, // optional -- pass in the Prism theme you're using
            darkTheme: darkCodeTheme, // optional -- pass in the Prism theme you're using
          },
        }
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  },
```

<Snippet file="learn_more/CommonSettings.mdx" />

<Snippet file="docusaurus/DocusaurusFAQ.mdx" />


# Add AI Search to your Docusaurus docs



## What is Docusaurus

[Docusaurus](https://docusaurus.io/) is an open-source documentation platform powered by MDX and React.

<Snippet file="InkeepGetApiKey.mdx" />

<Snippet file="docusaurus/InkeepQuickStart.mdx" />

## Define the widget

Аdd the search bar as a theme in your `docusaurus.config.js` file:

```js docusaurus.config.js
themes: ["@inkeep/docusaurus/searchBar"],
```

<Note>
  If you are already using `Algolia DocSearch` provided by Docusaurus by default, it will be replaced by our widget.
</Note>

### Сonfiguration settings

Next, add the below to the `themeConfig` property:

```js docusaurus.config.js
  //..
  themeConfig: {
    inkeepConfig: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // required -- your brand color, the widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
          syntaxHighlighter: {
            lightTheme: lightCodeTheme, // optional -- pass in the Prism theme you're using
            darkTheme: darkCodeTheme, // optional -- pass in the Prism theme you're using
          },
        }
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // optional -- use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  },
```

<Snippet file="learn_more/CommonSettings.mdx" />

<Note>
  If you have already created a custom `SearchBar` component (for example via `swizzle eject`) this will need to be removed in order to use our Search Bar.
</Note>

<Snippet file="docusaurus/DocusaurusFAQ.mdx" />


# Add AI Chat to your Framer website



<Snippet file="framer/QuickStartFramer.mdx" />

## Initialize the widget

Next, paste the script below into the **End of `<head>` tag** section:

```html
<script defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = function () {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) addInkeepWidget(); // initialize the widget
  });
</script>
```

## Save custom code

Click on the **Save** button. Navigate to your web application  and reload the page, to make sure the widget is installed correctly.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your Framer website



<Snippet file="framer/QuickStartFramer.mdx" />

## Creating the container

Add a widget container to the page using the built-in **Framer** functionality.

1.  Select an area to add a container
2.  Select the **Embed** item in the **Utility** section

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/framer/framer-embed-element.png" />

3.  Add an html element to the page

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/framer/framer-add-container.png" />

4.  Specify the ID of the container to be `inkeepSearchBar`

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/framer/framer-set-container-id.png" />

## Initialize the widget

Next, paste the script below into the **End of `<head>` tag** section:

```html
<script defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "SearchBar",
      targetElement: document.getElementById("inkeepSearchBar"),
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
          theme: {
            // stylesheetUrls: ["/path/to/stylesheets"], // optional
            // ...optional settings,
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  const observer = new MutationObserver((mutationsList, observer) => {
    const inkeepPortals = document.getElementsByTagName("inkeep-portal");

    Array.from(inkeepPortals)?.forEach((inkeepPortal) => {
      inkeepPortal.remove();
    });

    addInkeepWidget(); // initialize the widget
  });

  observer.observe(document.head, { attributes: true, subtree: true });
</script>
```

## Save custom code

Click on the **Save** button. Navigate to your web application and reload the page, to make sure the widget is installed correctly.

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add Inkeep's UI components to your Fumadocs



<Snippet file="fumadocs/QuickStartFumadocs.mdx" />

## Initialize the widget

Create an `inkeep-script.tsx` client component in your Fumadocs project.

```jsx inkeep-script.tsx
"use client";

declare global {
  interface Window {
    Inkeep: any;
  }
}

import Script from "next/script";

export function InkeepScript() {
  return (
    <Script
      id="inkeep-script"
      src="https://unpkg.com/@inkeep/uikit-js@0.3.14/dist/embed.js"
      type="module"
      strategy="afterInteractive"
      onReady={() => {
        window.Inkeep().embed({
          componentType: "ChatButton",
          colorModeSync: {
            observedElement: document.documentElement,
            isDarkModeCallback: (el) => {
              return el.classList.contains("dark");
            },
            colorModeAttribute: "class",
          },
          properties: {
            chatButtonType: "PILL",
            baseSettings: {
              apiKey: "INKEEP_API_KEY", // required
              integrationId: "INKEEP_INTEGRATION_ID", // required
              organizationId: "INKEEP_ORGANIZATION_ID", // required
              primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
              organizationDisplayName: "Inkeep",
              // ...optional settings
            },
            modalSettings: {
              // optional settings
            },
            searchSettings: {
              // optional settings
            },
            aiChatSettings: {
              // optional settings
              botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
              quickQuestions: [
                "Example question 1?",
                "Example question 2?",
                "Example question 3?",
              ],
            },
          },
        });
      }}
    />
  );
}
```

Then import it in the `<body />` of your root `layout.tsx` file.

```jsx layout.tsx {23}
import { InkeepScript } from "@/app/inkeep-script";
import { SearchBar } from "@/app/SearchBar";
import { RootProvider } from "fumadocs-ui/provider";
import "fumadocs-ui/style.css";
import { Inter } from "next/font/google";
import Script from "next/script";
import type { ReactNode } from "react";

const inter = Inter({
  subsets: ["latin"],
});

export default function Layout({ children }: { children: ReactNode }) {
  return (
    <html lang="en" className={inter.className} suppressHydrationWarning>
      <body
        style={{
          display: "flex",
          flexDirection: "column",
          minHeight: "100vh",
        }}
      >
        <InkeepScript />
        <RootProvider>{children}</RootProvider>
      </body>
    </html>
  );
}
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Chat to your Gatsby app



<Snippet file="gatsby/QuickStartGatsby.mdx" />

<Snippet file="InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepChatButton.tsx` file for the [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.

<CodeGroup>
  ```jsx .jsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";

  export default function InkeepChatButton() {
    const [ChatButton, setChatButton] = useState(null);

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadChatButton = async () => {
        try {
          const { InkeepChatButton } = await import("@inkeep/uikit");
          setChatButton(() => InkeepChatButton);
        } catch (error) {
          console.error("Failed to load ChatButton:", error);
        }
      };

      loadChatButton();
    }, []);

    const chatButtonProps = {
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return ChatButton && <ChatButton {...chatButtonProps} />;
  }
  ```

  ```tsx .tsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";
  import type { InkeepChatButtonProps } from "@inkeep/uikit";

  export default function InkeepChatButton() {
    const [ChatButton, setChatButton] =
      useState<(e: InkeepChatButtonProps) => JSX.Element>();

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadChatButton = async () => {
        try {
          const { InkeepChatButton } = await import("@inkeep/uikit");
          setChatButton(() => InkeepChatButton);
        } catch (error) {
          console.error("Failed to load ChatButton:", error);
        }
      };

      loadChatButton();
    }, []);

    const chatButtonProps: InkeepChatButtonProps = {
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return ChatButton && <ChatButton {...chatButtonProps} />;
  }
  ```
</CodeGroup>

<Snippet file="learn_more/ChatButtonSettingsReact.mdx" />


# Add an Embedded AI Chat to your Gatsby app



<Snippet file="gatsby/QuickStartGatsby.mdx" />

<Snippet file="InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepEmbeddedChat.tsx` file for the [`Embedded Chat`](https://docs.inkeep.com/ui-components/react/embedded-chat) component.

<CodeGroup>
  ```jsx .jsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";

  export default function InkeepEmbeddedChat() {
    const [EmbeddedChat, setEmbeddedChat] = useState(null);

    const { baseSettings, aiChatSettings } = useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadEmbeddedChat = async () => {
        try {
          const { InkeepEmbeddedChat } = await import("@inkeep/uikit");
          setEmbeddedChat(() => InkeepEmbeddedChat);
        } catch (error) {
          console.error("Failed to load EmbeddedChat:", error);
        }
      };

      loadEmbeddedChat();
    }, []);

    const embeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return EmbeddedChat && <EmbeddedChat {...embeddedChatProps} />;
  }
  ```

  ```tsx .tsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";
  import type { InkeepChatButtonProps } from "@inkeep/uikit";

  export default function InkeepEmbeddedChat() {
    const [EmbeddedChat, setEmbeddedChat] = useState(null);

    const { baseSettings, aiChatSettings } = useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadEmbeddedChat = async () => {
        try {
          const { InkeepEmbeddedChat } = await import("@inkeep/uikit");
          setEmbeddedChat(() => InkeepEmbeddedChat);
        } catch (error) {
          console.error("Failed to load EmbeddedChat:", error);
        }
      };

      loadEmbeddedChat();
    }, []);

    const embeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return EmbeddedChat && <EmbeddedChat {...embeddedChatProps} />;
  }
  ```
</CodeGroup>

<Snippet file="learn_more/EmbeddedChatSettingReact.mdx" />


# Add AI Search to your Gatsby app



<Snippet file="gatsby/QuickStartGatsby.mdx" />

<Snippet file="InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepSearchBar.tsx` file for the [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.

<CodeGroup>
  ```jsx .jsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";

  export default function InkeepSearchBar() {
    const [SearchBar, setSearchBar] = useState(null);

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadSearchBar = async () => {
        try {
          const { InkeepSearchBar } = await import("@inkeep/uikit");
          setSearchBar(() => InkeepSearchBar);
        } catch (error) {
          console.error("Failed to load SearchBar:", error);
        }
      };

      loadSearchBar();
    }, []);

    const searchBarProps = {
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return SearchBar && <SearchBar {...searchBarProps} />;
  }
  ```

  ```tsx .tsx
  import * as React from "react";
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";
  import type { InkeepSearchBarProps } from "@inkeep/uikit";

  export default function InkeepSearchBar() {
    const [SearchBar, setSearchBar] =
      useState<(e: InkeepSearchBarProps) => JSX.Element>();

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadSearchBar = async () => {
        try {
          const { InkeepSearchBar } = await import("@inkeep/uikit");
          setSearchBar(() => InkeepSearchBar);
        } catch (error) {
          console.error("Failed to load SearchBar:", error);
        }
      };

      loadSearchBar();
    }, []);

    const searchBarProps: InkeepSearchBarProps = {
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return SearchBar && <SearchBar {...searchBarProps} />;
  }
  ```
</CodeGroup>

<Snippet file="learn_more/SearchBarSettingsReact.mdx" />


# Add AI Chat to your GitBook docs



<Snippet file="gb/QuickStartGitBook.mdx" />

## Installing widget for your website

1.  Navigate to [GitBook](https://app.gitbook.com/integrations/inkeep)
2.  Click **Install**
3.  After installation, navigate to **Docs sites** tab and enable the widget for your desired site

## Initialize the widget

Navigate to the **Configuration** tab to customize your settings.

The **Base Widget Setting** is required for the widget to function.
Your configuration should resemble the example below:

```json baseSettings
{
  "apiKey": "INKEEP_API_KEY",
  "integrationId": "INKEEP_INTEGRATION_ID",
  "organizationId": "INKEEP_ORGANIZATION_ID",
  "primaryBrandColor": "#26D6FF",
  "organizationDisplayName": "Inkeep"
}
```

Once the configuration is applied, the widget will appear on the site you enabled the widget for.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Chat using Google Tag Manager



## What is Google Tag Manager

Google Tag Manager (GTM) lets you manage and deploy custom JavaScript, without editing your website's code. This simplifies updates without needing developer involvement.

## How to integrate with Inkeep

To add the Inkeep Chat Button using Google Tag Manager (GTM), we'll use a modified version of the standard JavaScript snippet. This approach includes scoping, dynamic loading, and controlled initialization for GTM compatibility.

Here's the GTM-friendly version of the Chat Button snippet after grabbing your API information:

<Snippet file="InkeepGetApiKey.mdx" />

## Define the component in Google Tag Manager

1.  Navigate to your [Google Tag Manager](https://tagmanager.google.com/) console
2.  Select your workspace and navigate to the Tags section
3.  Create a new Tag.
4.  For Tag Configuration, choose **Custom HTML**. And upload the following for the Chat Button:

```HTML
<script>
// Scoping
(function() {
    // Dynamic loading of the Inkeep widget script
    var script = document.createElement('script');
    script.type = 'module';
    script.src = 'https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js';
    script.defer = true;
    document.head.appendChild(script);

    // Controlled initialization. Initializes the Inkeep widget once the script is loaded
    script.onload = function() {
        var inkeepWidget = Inkeep().embed({
            componentType: "ChatButton",
            properties: {
                baseSettings: {
                    apiKey: "",
                    integrationId: "",
                    organizationId: "",
                    primaryBrandColor: ""
                },
                aiChatSettings: {
                    chatSubjectName: "",
                    botAvatarSrcUrl: "",
                    getHelpCallToActions: [],
                    quickQuestions: []
                }
            }
        });
    };
})();
</script>
```

5.  Update this script with your API key and desired settings.
6.  For Triggering, choose **All Pages**.
7.  Click save to have the Chat Button appear on your GTM synced website

## Other Snippets

For the other integrations please refer to the JS Snippet examples in the UI Components Page section

*   [Search Bar](/ui-components/js-snippet/search-bar)
*   [Embedded Chat](/ui-components/js-snippet/embedded-chat)
*   [Custom Trigger](/ui-components/js-snippet/custom-trigger)


# Add AI Chat to your Hugo docs



## Overview

In this integration example, we will be integrating Inkeep widget into [Ananke](https://themes.gohugo.io/themes/gohugo-theme-ananke/) theme, which is the default theme used as an example with Hugo.

## What is Hugo

[Hugo](https://gohugo.io/) is a popular open-source static site generator often used for documentation.

<Snippet file="InkeepGetApiKey.mdx" />

## Create the baseof.html file

Create an `baseof.html` file at the path `layouts/_default`

```bash
touch layouts/_default/baseof.html
```

Copy the contents of the `baseof.html` file:

```bash
cp themes/ananke/layouts/_default/baseof.html layouts/_default/baseof.html
```

<Note> Depending on your Hugo theme, this step may differ. The goal is to be able to insert custom scripts into your sites `<head>` tag.</Note>

### Load the script files

In the `baseof.html` file, locate the **head** tag and paste the scripts below at the end:

```html baseof.html
<head>
  <!-- Other code -->
  <script
    src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
    type="module"
    defer
  ></script>

  <script src="/js/addInkeep.js" defer></script>
</head>
```

## Create the addInkeep.js script

Create an `addInkeep.js` file in your `static/js` folder.

```bash
touch static/js/addInkeep.js
```

Now, configure the chat button component.

```js addInkeep.js
const inkeepWidget = Inkeep().embed({
  componentType: "ChatButton",
  properties: {
    chatButtonType: "PILL",
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Chat to your Docsy docs



<Snippet file="hugo/QuickStartDocsy.mdx" />

Now, configure the chat button component.

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const inkeepWidget = Inkeep().embed({
  componentType: "ChatButton",
  properties: {
    chatButtonType: "PILL",
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your Docsy docs



<Snippet file="hugo/QuickStartDocsy.mdx" />

<Note>Disable the default search in the `config.toml (or hugo.toml)` file, by setting the `offlineSearch` parameter to **false**.</Note>

Now, create a container and configure the search bar component.

```js addInkeep.js
// Create an HTML element that the Inkeep widget will be inserted into.
const nav = document.querySelector("nav");
const sidebar = document.getElementById("td-sidebar-menu");

const inkeepNavDiv = document.createElement("div");
inkeepNavDiv.id = "navSearchBar";
nav.appendChild(inkeepNavDiv);

const inkeepSidebarDiv = document.createElement("div");
inkeepSidebarDiv.id = "sideSearchBar";
sidebar && sidebar.prepend(inkeepSidebarDiv);

// Function for initializating the widget.
const addInkeepWidget = ({
  targetElement,
  stylesheetUrls,
  isShortcutKeyEnabled,
}) => {
  // Embed the widget using the `Inkeep.embed()` function.
  const inkeepWidget = Inkeep().embed({
    componentType: "SearchBar",
    targetElement,
    properties: {
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
        theme: {
          stylesheetUrls,
          // ...optional settings
        },
      },
      modalSettings: {
        // optional settings
        isShortcutKeyEnabled,
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  });
};

sidebar &&
  addInkeepWidget({
    targetElement: document.getElementById("sideSearchBar"),
    stylesheetUrls: ['/path/to/stylesheets'], // optional
    isShortcutKeyEnabled: false,
  });

addInkeepWidget({
  targetElement: document.getElementById("navSearchBar"),
  stylesheetUrls: ['/path/to/stylesheets'], // optional
  isShortcutKeyEnabled: true,
});
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add Inkeep's UI components to your Mintlify docs



## Overview

[Mintlify](https://mintlify.com) is a managed documentation platform with a fresh, modern look.

You can leverage Inkeep to extend the default functionality provided by the Mintlify platform with:

*   [Additional sources](/sources/onboard-new-sources), like GitHub repos and community forums.
*   Extensible search bar, chat button, and embedded chat UI components you can add to your help desk, app, docs, or marketing site.
*   Slack and Discord bots for your community or internal team channels
*   AI tools for support teams

To add Inkeep's search bar or "Ask AI" chat button to your Mintlify docs, you can add a script file to the root of your docs repo. Check out the search bar and chat button on this page for an example.

<Snippet file="InkeepGetApiKey.mdx" />

## Add a script to your repo

Create an `inkeep.js` file at the root of your documentation GitHub repo like the example below. Customize `inkeepSettings` with your API key and other [customizations](/ui-components/common-settings).

By default, the below script adds both Inkeep's search bar and AI chat button.

```js inkeep.js
// customize
const inkeepSettings = {
  baseSettings: {
    apiKey: "INKEEP_API_KEY", // required
    integrationId: "INKEEP_INTEGRATION_ID", // required
    organizationId: "INKEEP_ORGANIZATION_ID", // required
    primaryBrandColor: "#26D6FF", // required -- your brand color, the color scheme is derived from this
    organizationDisplayName: "Inkeep",
    // ...optional settings
  },
  aiChatSettings: {
    // ...optional settings
    botAvatarSrcUrl:
      "https://mydomain.com/mylogo.svg",
    quickQuestions: [
      "Example question 1?",
      "Example question 2?",
      "Example question 3?",
    ],
  },
  modalSettings: {
    isShortcutKeyEnabled: false, // disable default cmd+k behavior
    // ...optional settings
  },
};

// The Mintlify search triggers, which we'll reuse to trigger the Inkeep modal
const searchButtonContainerIds = [
  "search-bar-entry",
  "search-bar-entry-mobile",
];

// Clone and replace, needed to remove existing event listeners
const clonedSearchButtonContainers = searchButtonContainerIds.map((id) => {
  const originalElement = document.getElementById(id);
  const clonedElement = originalElement.cloneNode(true);
  originalElement.parentNode.replaceChild(clonedElement, originalElement);

  return clonedElement;
});

// Load the Inkeep component library
const inkeepScript = document.createElement("script");
inkeepScript.type = "module";
inkeepScript.src =
  "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
document.body.appendChild(inkeepScript);

// Once the Inkeep library is loaded, instantiate the UI components
inkeepScript.addEventListener("load", function () {
  // Customization settings

  // for syncing with dark mode
  const colorModeSettings = {
    observedElement: document.documentElement,
    isDarkModeCallback: (el) => {
      return el.classList.contains("dark");
    },
    colorModeAttribute: "class",
  };

  // Instantiate the 'Ask AI' pill chat button. Optional.
  Inkeep().embed({
    componentType: "ChatButton",
    colorModeSync: colorModeSettings,
    properties: inkeepSettings,
  });

  // Instantiate the search bar modal
  const inkeepSearchModal = Inkeep({
    ...inkeepSettings.baseSettings,
  }).embed({
    componentType: "CustomTrigger",
    colorModeSync: colorModeSettings,
    properties: {
      ...inkeepSettings,
      isOpen: false,
      onClose: () => {
        inkeepSearchModal.render({
          isOpen: false,
        });
      },
    },
  });

  // When the Mintlify search bar elements are clicked, open the Inkeep search modal
  clonedSearchButtonContainers.forEach((trigger) => {
    trigger.addEventListener("click", function () {
      inkeepSearchModal.render({
        isOpen: true,
      });
    });
  });

  // Open the Inkeep Modal with cmd+k
  window.addEventListener(
    "keydown",
    (event) => {
      if (
        (event.metaKey || event.ctrlKey) &&
        (event.key === "k" || event.key === "K")
      ) {
        event.stopPropagation();
        inkeepSearchModal.render({ isOpen: true });
        return false;
      }
    },
    true
  );
});

```

<Note>
  If custom JavaScript is not part of your plan, [let us know](mailto:support@inkeep.com), and we'll work with the Mintlify team to enable it for you under your current plan.
</Note>


# Add AI Chat to your MkDocs docs



<Snippet file="mkdocs/QuickStartMkDocs.mdx" />

## Initialize the widget

In docs folder create a custom js file, for example `inkeep-button.js`. Add path to this file to the `mkdocs.yml`:

```yml mkdocs.yml
extra_javascript:
  - inkeep-button.js
```

Then add the button:

```js inkeep-button.js
document.addEventListener("DOMContentLoaded", () => {
   // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    addInkeepWidget(); // initialize the widget
  });
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search & Chat Button to your MkDocs docs



<Snippet file="mkdocs/QuickStartMkDocs.mdx" />

## Initialize the widget

In docs folder create a custom js file, for example `button-and-search-bar.js`. Add path to this file to the `mkdocs.yml` and disable default search bar with `plugins: []`:

```yml mkdocs.yml
extra_javascript:
  - button-and-search-bar.js
plugins: []
```

Then add the button and search bar:

```js button-and-search-bar.js
document.addEventListener("DOMContentLoaded", () => {
  // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Create a new div for the Inkeep search bar
  const inkeepDiv = document.createElement("div");
  inkeepDiv.id = "inkeepSearchBar";

  // Get the header element where you want to place the Inkeep search bar
  const headerElement = document.querySelector("#navbar-collapse");
  if (headerElement) {
    headerElement.appendChild(inkeepDiv);
  }

  // configure and initialize the widget
  const addInkeepWidget = (componentType, targetElementId) => {
    const inkeepWidget = Inkeep().embed({
      componentType,
      ...(componentType !== "ChatButton"
        ? { targetElement: targetElementId }
        : {}),
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dracula" || currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode-scheme",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
          theme: {
            // stylesheetUrls: ["/path/to/stylesheets"], // optional
            // ...optionalSettings,
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    const widgetContainer = document.getElementById("inkeepSearchBar");

    addInkeepWidget("ChatButton");
    widgetContainer && addInkeepWidget("SearchBar", "#inkeepSearchBar");
  });
});
```

<Snippet file="learn_more/CommonSettings.mdx" />


# Add AI Search to your MkDocs docs



<Snippet file="mkdocs/QuickStartMkDocs.mdx" />

## Initialize the widget

In docs folder create a custom js file, for example `search-bar.js`. Add path to this file to the `mkdocs.yml` and disable default search bar with `plugins: []`:

```yml mkdocs.yml
extra_javascript:
  - search-bar.js
plugins: []
```

Then add the search bar:

```js search-bar.js
document.addEventListener("DOMContentLoaded", () => {
  // Create a new div for the Inkeep search bar
  const inkeepDiv = document.createElement("div");
  inkeepDiv.id = "inkeepSearchBar";

  // Get the header element where you want to place the Inkeep search bar
  const headerElement = document.querySelector(".navbar-nav");
  if (headerElement) {
    headerElement.parentNode.insertBefore(inkeepDiv, headerElement.nextSibling);
  }

  // Load the Inkeep script
  const inkeepScript = document.createElement("script");
  inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
  inkeepScript.type = "module";
  inkeepScript.defer = true;
  document.head.appendChild(inkeepScript);

  // Initialize the Inkeep widget after the script loads
  inkeepScript.addEventListener("load", () => {
    Inkeep().embed({
      componentType: "SearchBar",
      targetElement: "#inkeepSearchBar",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-md-color-scheme");
          return currentTheme === "dracula" || currentTheme === "dark";
        },
        colorModeAttribute: "data-md-color-scheme",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF",
          organizationDisplayName: "Inkeep",
        },
      },
    });
  });
});
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Next.js app



<Snippet file="nextjs/QuickStartNextJS.mdx" />

<Snippet file="nextjs/InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepChatButton.tsx` file for our [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.

<Snippet file="nextjs/NoteAppRouterNextJS.mdx" />

<Snippet file="nextjs/InkeepChatButtonDefineNextJS.mdx" />

<Snippet file="learn_more/ChatButtonSettingsReact.mdx" />


# Add a Custom Trigger to your Next.js app

Trigger the Inkeep modal with a custom button in your Next.js app.

<Snippet file="nextjs/QuickStartNextJS.mdx" />

<Snippet file="nextjs/InkeepWidgetSettings.mdx" />

### Define the component

Create an `InkeepCustomTrigger.tsx` file for our [`Custom Trigger`](https://docs.inkeep.com/ui-components/react/custom-trigger) component. `<button>` represents whatever UI element you'd like to use to trigger the Inkeep Modal.

<Snippet file="nextjs/NoteAppRouterNextJS.mdx" />

<CodeGroup>
  ```jsx .jsx
  import dynamic from 'next/dynamic';
  import { useCallback, useState } from 'react';
  import useInkeepSettings from '@/utils/useInkeepSettings';

  const CustomTrigger = dynamic(
    () => import('@inkeep/uikit').then((mod) => mod.InkeepCustomTrigger),
    {
      ssr: false,
    },
  );

  function InkeepCustomTrigger() {
    const [isOpen, setIsOpen] = useState(false);
    const {
      baseSettings, aiChatSettings, searchSettings, modalSettings
    } = useInkeepSettings();

    const handleClose = useCallback(() => {
      console.log("Modal closed");
      setIsOpen(false);
    }, []);

    const customTriggerProps = {
      isOpen,
      onClose: handleClose,
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return (
      <div>
        <button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
        <CustomTrigger {...customTriggerProps} />
      </div>
    );
  }

  export default InkeepCustomTrigger;
  ```

  ```tsx .tsx
  import dynamic from 'next/dynamic';
  import { useCallback, useState } from 'react';
  import { InkeepCustomTriggerProps } from '@inkeep/uikit';
  import useInkeepSettings from '@/utils/useInkeepSettings';

  const CustomTrigger = dynamic(
    () => import('@inkeep/uikit').then((mod) => mod.InkeepCustomTrigger),
    {
      ssr: false,
    },
  );

  function InkeepCustomTrigger() {
    const [isOpen, setIsOpen] = useState(false);
    const {
      baseSettings, aiChatSettings, searchSettings, modalSettings
    } = useInkeepSettings();

    const handleClose = useCallback(() => {
      console.log("Modal closed");
      setIsOpen(false);
    }, []);

    const customTriggerProps: InkeepCustomTriggerProps = {
      isOpen,
      onClose: handleClose,
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return (
      <div>
        <button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
        <CustomTrigger {...customTriggerProps} />
      </div>
    );
  }

  export default InkeepCustomTrigger;
  ```
</CodeGroup>

<Snippet file="learn_more/CustomTriggerSettingsReact.mdx" />


# Add an Embedded Chat to your Next.js app

Add AI chat to your Next.js application.

<Snippet file="nextjs/QuickStartNextJS.mdx" />

<Snippet file="nextjs/InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepEmbeddedChat.tsx` component file for our [`Embedded Chat`](https://docs.inkeep.com/ui-components/react/embedded-chat) component.

<Snippet file="nextjs/NoteAppRouterNextJS.mdx" />

<CodeGroup>
  ```jsx .jsx
  import dynamic from 'next/dynamic';
  import useInkeepSettings from '@/utils/useInkeepSettings';

  const EmbeddedChat = dynamic(
    () => import('@inkeep/uikit').then((mod) => mod.InkeepEmbeddedChat),
    {
      ssr: false,
      // loading: () => <div>loading...</div>, // optional: loading animation component
    },
  );

  function InkeepEmbeddedChat() {
    const {
      baseSettings, aiChatSettings,
    } = useInkeepSettings();

    const embeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return <EmbeddedChat {...embeddedChatProps} />;
  }

  export default InkeepEmbeddedChat;

  ```

  ```tsx .tsx
  import dynamic from 'next/dynamic';
  import { InkeepEmbeddedChatProps } from '@inkeep/uikit';
  import useInkeepSettings from '@/utils/useInkeepSettings';

  const EmbeddedChat = dynamic(
    () => import('@inkeep/uikit').then((mod) => mod.InkeepEmbeddedChat),
    {
      ssr: false,
      // loading: () => <div>loading...</div>, // optional: loading animation component
    },
  );

  function InkeepEmbeddedChat() {
    const {
      baseSettings, aiChatSettings,
    } = useInkeepSettings();

    const embeddedChatProps: InkeepEmbeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return <EmbeddedChat {...embeddedChatProps} />;
  }

  export default InkeepEmbeddedChat;
  ```
</CodeGroup>

<Snippet file="learn_more/EmbeddedChatSettingReact.mdx" />


# Next 15

Using the uikit with Next 15 and React 19.

The `@inkeep/uikit` package supports React 18, in order to use the Inkeep components with Next 15 and React 19 you can install the `@inkeep/uikit-js` package.

<Snippet file="InkeepGetApiKey.mdx" />

<Snippet file="nextjs/InkeepEnvDefineNextJS.mdx" />

<Snippet file="InstallTheJSWidgetLibrary.mdx" />

#### Example

To ensure the uikit is only loaded client side, you will need to import the library asynchronously within a useEffect hook. This example uses the Chat Button component but the same logic can be applied to any of the other components. See [here](/ui-components/js-snippet) for additional details on using the `uikit-js` package.

```tsx
'use client';

import { useEffect, useRef } from "react";

const baseSettings = {
  integrationId: process.env.NEXT_PUBLIC_INKEEP_API_KEY,
  apiKey: process.env.NEXT_PUBLIC_INKEEP_INTEGRATION_ID,
  organizationId: process.env.NEXT_PUBLIC_INKEEP_ORGANIZATION_ID,
  primaryBrandColor: "#000000",
};


export const ChatButton = () => {
  const chatButtonRef = useRef<any>(null);

  useEffect(() => {
    const loadInkeepJS = async () => {
      const inkeepJS = await import("@inkeep/uikit-js");
        const inkeep = inkeepJS.Inkeep(baseSettings);
        chatButtonRef.current = inkeep.embed({
          componentType: "ChatButton",
          properties: {
            chatButtonType: "PILL",
            baseSettings,
            aiChatSettings: {
              quickQuestions: ["How to get started?"],
            },
          },
        });
    };
    loadInkeepJS();
  }, []);

  return null;
};
```


# Add AI Search to your Next.js app



<Snippet file="nextjs/QuickStartNextJS.mdx" />

<Snippet file="nextjs/InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepSearchBar.tsx` component file for our [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.

<Snippet file="nextjs/NoteAppRouterNextJS.mdx" />

<Snippet file="nextjs/InkeepSearchBarDefineNextJS.mdx" />

<Snippet file="learn_more/SearchBarSettingsReact.mdx" />


# Add AI Chat to your Nextra docs



<Snippet file="nextra/QuickStartNextra.mdx" />

<Snippet file="nextra/InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepChatButton.tsx` file for the [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.

<Snippet file="InkeepChatButtonDefineReact.mdx" />

### Modify footer

To add a widget to the page, paste the code shown below into the `theme.config.tsx` file:

```tsx theme.config.tsx
//...
footer: {
  component: () => <InkeepChatButton />
}
```

<Snippet file="learn_more/ChatButtonSettingsReact.mdx" />


# Add AI Search & Chat Button to your Nextra docs



<Snippet file="nextra/QuickStartNextra.mdx" />

<Snippet file="nextra/InkeepWidgetSettings.mdx" />

### Define the components

Next, create component files `InkeepChatButton.tsx` and `InkeepSearchBar.tsx`

#### ChatButton

<Snippet file="InkeepChatButtonDefineReact.mdx" />

#### SearchBar

<Snippet file="InkeepSearchBarDefineReact.mdx" />

### Modify theme config

To add widgets to the page, paste the code shown below into the `theme.config.tsx` file:

```tsx theme.config.tsx
//...
footer: {
  component: () => <InkeepChatButton />
},
search: {
  component: () => <InkeepSearchBar />
}
```

<Snippet file="learn_more/CommonSettings.mdx" />


# Add a Custom Trigger to your Nextra docs



<Snippet file="nextra/QuickStartNextra.mdx" />

<Snippet file="nextra/InkeepWidgetSettings.mdx" />

### Define the component

Create an `InkeepCustomTrigger.tsx` file for the [`Custom Trigger`](https://docs.inkeep.com/ui-components/react/custom-trigger) component.

<CodeGroup>
  ```jsx .jsx
  import { useCallback, useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";

  export default function InkeepCustomTrigger() {
    const [isOpen, setIsOpen] = useState(false);
    const [CustomTirgger, setCustomTrigger] = useState();

    const handleClose = useCallback(() => {
      console.log("Modal closed");
      setIsOpen(false);
    }, []);

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadCustomTrigger = async () => {
        try {
          const { InkeepCustomTrigger } = await import("@inkeep/uikit");
          setCustomTrigger(() => InkeepCustomTrigger);
        } catch (error) {
          console.error("Failed to load CustomTirgger:", error);
        }
      };

      loadCustomTrigger();
    }, []);

    const customTriggerProps = {
      isOpen,
      onClose: handleClose,
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return (
      <div>
        <button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
        {CustomTirgger && <CustomTirgger {...customTriggerProps} />}
      </div>
    )
  }

  ```

  ```tsx .tsx
  import { useCallback, useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";
  import type { InkeepCustomTriggerProps } from "@inkeep/uikit";

  export default function InkeepCustomTrigger() {
    const [isOpen, setIsOpen] = useState(false);
    const [CustomTirgger, setCustomTrigger] =
      useState<(e: InkeepCustomTriggerProps) => JSX.Element>();

    const handleClose = useCallback(() => {
      console.log("Modal closed");
      setIsOpen(false);
    }, []);

    const { baseSettings, aiChatSettings, searchSettings, modalSettings } =
      useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadCustomTrigger = async () => {
        try {
          const { InkeepCustomTrigger } = await import("@inkeep/uikit");
          setCustomTrigger(() => InkeepCustomTrigger);
        } catch (error) {
          console.error("Failed to load CustomTirgger:", error);
        }
      };

      loadCustomTrigger();
    }, []);

    const customTriggerProps: InkeepCustomTriggerProps = {
      isOpen,
      onClose: handleClose,
      baseSettings,
      aiChatSettings,
      searchSettings,
      modalSettings,
    };

    return (
      <div>
        <button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
        {CustomTirgger && <CustomTirgger {...customTriggerProps} />}
      </div>
    )
  }

  ```
</CodeGroup>

### Use the component

Add a widget component to the page:

```jsx
import InkeepCustomTrigger from "@/components/InkeepCustomTrigger";

export default function MyApp() {
  return (
    <>
      <InkeepCustomTrigger />
    </>
  )
}
```

<Snippet file="learn_more/CustomTriggerSettingsReact.mdx" />


# Add an Embedded AI Chat to your Nextra docs



<Snippet file="nextra/QuickStartNextra.mdx" />

<Snippet file="nextra/InkeepWidgetSettings.mdx" />

### Define the component

Create an `InkeepEmbeddedChat.tsx` file for the [`Embedded Chat`](https://docs.inkeep.com/ui-components/react/embedded-chat) component.

<CodeGroup>
  ```jsx .jsx
  import { useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";

  export default function InkeepEmbeddedChat() {
    const [EmbeddedChat, setEmbeddedChat] = useState(null);

    const { baseSettings, aiChatSettings } = useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadEmbeddedChat = async () => {
        try {
          const { InkeepEmbeddedChat } = await import("@inkeep/uikit");
          setEmbeddedChat(() => InkeepEmbeddedChat);
        } catch (error) {
          console.error("Failed to load EmbeddedChat:", error);
        }
      };

      loadEmbeddedChat();
    }, []);

    const embeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return EmbeddedChat && <EmbeddedChat {...embeddedChatProps} />;
  }
  ```

  ```tsx .tsx
  import { ReactNode, useEffect, useState } from "react";
  import useInkeepSettings from "@/utils/useInkeepSettings";
  import type { InkeepEmbeddedChatProps } from "@inkeep/uikit";

  export default function InkeepEmbeddedChat() {
    const [EmbeddedChat, setEmbeddedChat] =
      useState<(e: InkeepEmbeddedChatProps) => JSX.Element | ReactNode>();

    const { baseSettings, aiChatSettings } = useInkeepSettings();

    // load the library asynchronously
    useEffect(() => {
      const loadEmbeddedChat = async () => {
        try {
          const { InkeepEmbeddedChat } = await import("@inkeep/uikit");
          setEmbeddedChat(() => InkeepEmbeddedChat);
        } catch (error) {
          console.error("Failed to load EmbeddedChat:", error);
        }
      };

      loadEmbeddedChat();
    }, []);

    const embeddedChatProps: InkeepEmbeddedChatProps = {
      baseSettings,
      aiChatSettings,
    };

    return EmbeddedChat && <EmbeddedChat {...embeddedChatProps} />;
  }
  ```
</CodeGroup>

### Use the component

Now to add the `InkeepEmbeddedChat.tsx` component follow these steps:

1.  Create a new page - **Ask AI ✨**

```bash
    touch pages/ask-ai.mdx
```

2.  Initialize the page in the `_meta.json` file:

```json _meta.json
{
  // ...
  "ask-ai": "Ask AI ✨"
  // ...
}
```

3.  Add a widget to the page:

```jsx ask-ai.mdx
# Ask AI ✨

import InkeepEmbeddedChat from "@/components/InkeepEmbeddedChat";

<InkeepEmbeddedChat />
```

<Snippet file="learn_more/EmbeddedChatSettingReact.mdx" />


# Add AI Search to your Nextra docs



<Snippet file="nextra/QuickStartNextra.mdx" />

<Snippet file="nextra/InkeepWidgetSettings.mdx" />

### Define the component

Next, create an `InkeepSearchBar.tsx` file for the [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.

<Snippet file="InkeepSearchBarDefineReact.mdx" />

### Replace default search bar

To add a widget to the page, paste the code shown below into the `theme.config.tsx` file:

```tsx theme.config.tsx
//...
search: {
  component: () => <InkeepSearchBar />
}
```

<Note>Search bar will be replaced by this widget by default</Note>

<Snippet file="learn_more/SearchBarSettingsReact.mdx" />


# Add AI Chat to your ReadMe docs



<Snippet file="readme/QuickStartReadMe.mdx" />

## Initialize the widget

Next, in the **FOOTER HTML** section add the below code to add the chat button widget:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = function () {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
     addInkeepWidget(); // initialize the widget    
  });
</script>
```

Press **Save** to apply your changes.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search & Chat Button to your ReadMe docs



<Snippet file="readme/QuickStartReadMe.mdx" />

## Initialize the widgets

Next, in the **FOOTER HTML** section add the below code to hide the default search bar:

```html
<style>
  .rm-SearchToggle {
    display: none;
  }
</style>
```

Then add the chat button and search bar:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");
  const readmeHeaderSearch = document.querySelector(".rm-Header-search");
  const inkeepDiv = document.createElement("div");
  inkeepDiv.id = "inkeepSearchBar";

  // configure and initialize the widget
  const addInkeepWidget = (componentType, targetElementId) => {
    const inkeepWidget = Inkeep().embed({
      componentType,
      targetElement: targetElementId,
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
          theme: {
            // stylesheetUrls: ['/path/to/stylesheets'], // optional
            // ...optionalSettings,
          },
        },
        modalSettings: {
          // optional settings
          openShortcutKey: "L"
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  const observer = new MutationObserver(() => {
  	readmeHeaderSearch.appendChild(inkeepDiv)
  });
  observer.observe(document.head, { childList: true });

  inkeepScript.addEventListener("load", () => {
    const widgetContainer = document.getElementById("inkeepSearchBar");
		    
    addInkeepWidget("ChatButton");
    widgetContainer && 
	    addInkeepWidget("SearchBar", "#inkeepSearchBar");
  });
</script>
```

Press **Save** to apply your changes.

<Snippet file="learn_more/CommonSettings.mdx" />


# Add AI Search to your ReadMe docs



<Snippet file="readme/QuickStartReadMe.mdx" />

## Initialize the widget

Next, in the **FOOTER HTML** section add the below code to hide the default search bar:

```html
<style>
  .rm-SearchToggle {
    display: none;
  }
</style>
```

Then add the search bar:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");
  const readmeHeaderSearch = document.querySelector(".rm-Header-search");
  const inkeepDiv = document.createElement("div");
  inkeepDiv.id = "inkeepSearchBar";
  
    // configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "SearchBar",
      targetElement: "#inkeepSearchBar",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
          theme: {
            // stylesheetUrls: ['/path/to/stylesheets'], // optional
            // ...optional settings
          },
        },
        modalSettings: {
          // optional settings
          openShortcutKey: "L"
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  const observer = new MutationObserver(() => {
  	readmeHeaderSearch.appendChild(inkeepDiv)
  });
  observer.observe(document.head, { childList: true });

  inkeepScript.addEventListener("load", () => {
		addInkeepWidget(); // initialize the widget
  });
</script>
```

Press **Save** to apply your changes.

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Redocly API docs



<Snippet file="redocly/QuickStartApiRegistry.mdx" />

Now, configure the chat button component.

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const inkeepWidget = Inkeep().embed({
  componentType: "ChatButton",
  properties: {
    chatButtonType: "PILL",
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to Redocly API docs



<Snippet file="redocly/QuickStartApiRegistry.mdx" />

Now, create a container and configure the search bar component.

```js addInkeep.js
// Create an HTML element that the Inkeep widget will be inserted into.
const searchBarParentNode = document.querySelector(".scrollbar-container");
const inkeepDiv = document.createElement("div");
inkeepDiv.id = "inkeep";
searchBarParentNode.prepend(inkeepDiv);

let inkeepWidget = null;
const targetElement = document.getElementById("inkeep");
const config = {
  componentType: "SearchBar",
  targetElement,
  properties: {
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
      theme: {
        // stylesheetUrls: ['/path/to/stylesheets'], // optional
        // ...optional settings
      },
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
};

// Wait for container to render
if (!inkeepWidget && targetElement) {
  // Embed the widget using the `Inkeep.embed()` function.
  inkeepWidget = Inkeep().embed(config);
}
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Redocly developer portal



<Snippet file="redocly/QuickStartDevPortal.mdx" />

Now, configure the chat button component.

```js addInkeep.js
// Function for adding a script to the page
const addScript = (src, type = "module", defer = true) => {
  const script = document.createElement("script");
  script.src = src;
  script.type = type;
  script.defer = defer;
  document.body.appendChild(script);
  return script;
};

// Function for customizing the widget configuration
const inkeepConfig = () => {
  return {
    componentType: "ChatButton",
    properties: {
      chatButtonType: "PILL", // <-- the "Pill" variation
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  };
};

// Function for initializating the widget 
const addInkeepWidget = () => {
  const inkeepWidgetScript = document.createElement("script");
  inkeepWidgetScript.defer = true;

  // Embed the widget using the `Inkeep.embed()` function.
  inkeepWidgetScript.innerHTML = `
    const config = (${inkeepConfig.toString()})();
    Inkeep().embed(config);
`;

  document.body.appendChild(inkeepWidgetScript);
};

// Adding the script and initializing the widget
const embedScript = addScript(
  "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
);

embedScript.addEventListener("load", () => {
  addInkeepWidget();
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your Redocly developer portal



<Snippet file="redocly/QuickStartDevPortal.mdx" />

Now, create a container and configure the search bar component.

```js addInkeep.js
// Function for adding a script to the page
const addScript = (src, type = "module", defer = true) => {
  const script = document.createElement("script");
  script.src = src;
  script.type = type;
  script.defer = defer;
  document.body.appendChild(script);
  return script;
};

// Function for customizing the widget configuration
const inkeepConfig = (targetElement) => ({
  componentType: "SearchBar",
  targetElement,
  properties: {
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
      theme: {
        // stylesheetUrls: ['/path/to/stylesheets'], // optional
        // ...optional settings
      },
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});

// Function for initializating the widget 
const addInkeepWidget = () => {
  const search = document.getElementById("search")?.parentNode;

  if (!search) return;

  search.style.height = "auto";
  search.innerHTML = "";

  Inkeep().embed(inkeepConfig(search));

  const observer = new MutationObserver((mutationsList, observer) => {
    const search = document.getElementById("search")?.parentNode;

    if (!search) return;

    const inkeepPortals = document.getElementsByTagName("inkeep-portal");

    Array.from(inkeepPortals)?.forEach((inkeepPortal) => {
      inkeepPortal.remove();
    });

    search.innerHTML = "";
    search.style.height = "auto";

    inkeepConfig(search).targetElement = search;
    
    // Embed the widget using the `Inkeep.embed()` function.
    Inkeep().embed(inkeepConfig(search));
  });

  observer.observe(document.head, { childList: true });
};

// Adding the script and initializing the widget
const inkeepWidgetScript = document.createElement("script");
inkeepWidgetScript.defer = true;
inkeepWidgetScript.innerHTML = addInkeepWidget.toString();

document.body.appendChild(inkeepWidgetScript);

const embedScript = addScript(
  "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
);

embedScript.addEventListener("load", addInkeepWidget);
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Remix app



<Snippet file="remix/QuickStartRemix.mdx" />

<Snippet file="remix/InkeepWidgetSettingsRemix.mdx" />

### Define the component

Next, create an `InkeepChatButton.tsx` file for the [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.

<Snippet file="InkeepChatButtonDefineReact.mdx" />

<Snippet file="learn_more/ChatButtonSettingsReact.mdx" />


# Add AI Search to your Remix app



<Snippet file="remix/QuickStartRemix.mdx" />

<Snippet file="remix/InkeepWidgetSettingsRemix.mdx" />

### Define the component

Next, create an `InkeepSearchBar.tsx` file for the [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.

<Snippet file="InkeepSearchBarDefineReact.mdx" />

<Snippet file="learn_more/SearchBarSettingsReact.mdx" />


# Add an AI chatbot to your Slack workspace or community.



## Overview

You can add Inkeep as a [Slack](https://slack.com/) app to your Slack community, customer Slack connect channels, or internal channels where your team collaborates.

## Get Workspace ID

To configure the bot, you'll need your Slack Workspace ID:

1.  Open the Slack client in your [browser](https://app.slack.com/client).
2.  Select the target workspace
3.  Copy the `{WORKSPACE_ID}` from the browser's navigation bar. The URL will be in the format `https://app.slack.com/client/{WORKSPACE_ID}/{CHANNEL_ID}`. **Workspace ID typically starts with a `T` and is in the format of `TXXXXXXXXXX`.**
4.  Alternatively, you can find the ID in the workspace settings.

## Create Integration

To use the Inkeep Slack bot, we first need to register it as an integration.

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Click on **Create Integration**
4.  From the dropdown menu, choose **Slack**
5.  Fill in the required fields **Name** and **Workspace ID**.
6.  (Optional) If you'd like the bot to only respond to pre-specified channels, deselect the **Is default for workspace** checkbox and add the specific channel IDs in **Enabled for channels** under **Advanced settings**
7.  Click on **Create**

<Note>
  If you need multiple **@Ask Inkeep** Slack bots for different projects on the same workspace, then specify the channel IDs that it is enabled for. Only one Slack integration can be the `Default` for a given workspace.
</Note>

## Add to workspace

To install the Inkeep Slack bot in your workspace:

1.  Click [here](https://install.inkeep.com/ask-slack)
2.  Select the desired workspace
3.  Confirm the installation.

### Create a channel (optional)

If you don't yet have a channel you'd like to add Inkeep to, you can create an `✨ask-ai` or similar channel. Move or pin it to the appropriate sections in your workspace for visibility.

This can be for your community members or for your own team members.

### Add to channels

Open the workspace you added the **Ask Inkeep** bot to.

Navigate to the channel you'd like to add Inkeep to.

Type `@Ask Inkeep` with an example question:

```
@Ask Inkeep How do I get started?
```

**Confirm** Slack's prompt to add the bot to the channel.

Repeat the steps for any other desired channels.

<Note> The Slack bot can be added to any type of channel, including private channels or Slack Connect channels with other organizations. To work, the "Ask Inkeep" bot *must* be added as a member of the channel. </Note>

### Use as an auto-reply bot

Instead of requiring a user to tag the bot with `@Ask Inkeep`, you can configure the Slack bot to automatically reply to all root-level messages in select Slack channels. You can leverage this mode in your Slack community or for internal `#support-triage` channels.

To configure:

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Select the Integration
4.  Expand **Advanced Settings**
5.  Configure the following:
    *   **Auto-reply in the below channels**: Specify the channel IDs where you want the bot to auto-reply
    *   **Reply message (optional)**: Enter a custom initial message for the bot to use when it's tagged or auto-replies
6.  (Optional) Adjust the tone of the bot by enabling any of these two options:

*   **AI Draft Mode**: Adjusts bot's tone for generating draft responses for team review. Useful when AI assists team rather than directly interacting with users.
*   **Human Reviewing Conversations**: Informs users that team members are monitoring conversations. Helps set expectations for response times and accuracy.

6.  Click **Save** to apply the changes

## Customize name and icon

You can customize the name of the bot and the image used with it.

### Upload an organization avatar (required)

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Settings** tab at root of your organization (`https://portal.inkeep.com/{orgAlias}/settings`)
3.  Under **Avatar**, select **Choose File** or if there's an existing file, hover over the existing avatar and click **Change**
4.  Upload a PNG (preferred) or JPG file you'd like to use as your avatar. Recommended dimension: 512x512. Note that SVGs are not supported by Slack

### Configure the Slack bot name

1.  In your Slack app, click on **Add apps** underneath the **Apps** section in your sidebar
2.  Select **Ask Inkeep**
3.  Click on **Configuration**
4.  Scroll to **Bot User** section and click on **Edit**
5.  Specify the desired name of the bot, e.g. `Ask CompanyAI`

Now, you and your users will be able to tag the bot using this name, e.g. `@Ask CompanyAI`.

<Note> When typing `@` and in a few other places, Slack will still display the default Inkeep icon. However, any posts made by the bot will show your actual avatar. Your custom name will always be displayed. </Note>

## Tag a team member

Sometimes, you want users to be able to escalate to a human for help if the user needs additional help or the bot is not able to answer.

To make this flow seamless, you can configure your Slack integration to show **Mark as resolved ✅** and **Ask for help 👋** buttons instead of the default 👍 👎.

When a user clicks on **Ask for help 👋**, the bot can tag users, user groups, or other bots.

To set up:

1.  Open the [Inkeep Dashboard](https://portal.inkeep.com)
2.  Navigate to the **Integrations** tab within the desired project
3.  Select the Slack Integration
4.  Expand **Advanced Settings**
5.  Under **When a user leaves negative feedback...**, click the dropdown menu
6.  Select **Tag a team member**
7.  Specify the **User IDs**, **Group IDs**, or **Bot IDs** you'd like to tag

You can get the IDs by left-clicking on a profile on Slack, selecting the more options button (often represented as three dots), and clicking on **Copy member ID** or **Copy group ID**.

To create a user group, see [this](https://slack.com/help/articles/212906697-Create-a-user-group) guide.

<Accordion title="Can I trigger other automations based on `Ask for help👋`">
  We recommend creating a custom bot, e.g. `@Triaging Bot`, that contains the logic you'd like to use for triggering custom down-stream workflows.
</Accordion>


# Add AI Chat to your Sphinx docs



<Snippet file="sphinx/QuickStartSphinx.mdx" />

Now, configure the chat button component.

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const inkeepWidget = Inkeep().embed({
  componentType: "ChatButton",
  properties: {
    chatButtonType: "PILL",
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your Sphinx docs



<Snippet file="sphinx/QuickStartSphinx.mdx" />

Now, create a container and configure the search bar component.

### Configure the component

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const inkeepWidget = Inkeep().embed({
  componentType: "SearchBar",
  targetElement: document.getElementById("inkeepSearchBar"),
  properties: {
    baseSettings: {
      apiKey: "INKEEP_API_KEY", // required
      integrationId: "INKEEP_INTEGRATION_ID", // required
      organizationId: "INKEEP_ORGANIZATION_ID", // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
      theme: {
        // stylesheetUrls: ['/path/to/stylesheets'], // optional
        // ...optional settings
      },
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
});
```

## Container element

Create a `searchbox.html` file in `source/_templates` and paste in the below::

<Note>
  This Inkeep `SearchBar` widget will replace the default search bar used on the website.
</Note>

```html searchbox.html
<div id="inkeepSearchBar"></div>
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your VitePress docs



<Snippet file="vitepress/QuickStartVitePress.mdx" />

Now, configure the chat button component.

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const config = {
  componentType: "ChatButton",
  // optional -- for syncing UI color mode
  colorModeSync: {
    observedElement: document.documentElement,
    isDarkModeCallback: (el) => {
      return el.classList.contains("dark");
    },
    colorModeAttribute: "class",
  },
  properties: {
    chatButtonType: "PILL",
    baseSettings: {
      apiKey: import.meta.env.VITE_INKEEP_API_KEY, // required
      integrationId: import.meta.env.VITE_INKEEP_INTEGRATION_ID, // required
      organizationId: import.meta.env.VITE_INKEEP_ORGANIZATION_ID, // required
      primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
};

const inkeepWidget = Inkeep().embed(config);
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your VitePress docs



<Snippet file="vitepress/QuickStartVitePress.mdx" />

Now, create a container and configure the search bar component.

### Configure the component

Next, embed the widget using the `Inkeep.embed()` function.

<Note>
  This will replace the default search bar used on the
  website.
</Note>

```js addInkeep.js
// Embed the widget using the `Inkeep.embed()` function.
const config = {
  componentType: "SearchBar",
  targetElement: document.querySelector(".search"),
  // optional -- for syncing UI color mode
  colorModeSync: {
    observedElement: document.documentElement,
    isDarkModeCallback: (el) => {
      return el.classList.contains("dark");
    },
    colorModeAttribute: "class",
  },
  properties: {
    baseSettings: {
      apiKey: import.meta.env.VITE_INKEEP_API_KEY, // required
      integrationId: import.meta.env.VITE_INKEEP_INTEGRATION_ID, // required
      organizationId: import.meta.env.VITE_INKEEP_ORGANIZATION_ID, // required
      primaryBrandColor: "#26D6FF", // your brand color, the widget color scheme is derived from this
      organizationDisplayName: "Inkeep",
      // ...optional settings
      theme: {
        // stylesheetUrls: ['/path/to/stylesheets'], // optional
        // ...optional settings
      },
    },
    modalSettings: {
      // optional settings
    },
    searchSettings: {
      // optional settings
    },
    aiChatSettings: {
      // optional settings
      botAvatarSrcUrl: "/img/logo.svg", // use your own bot avatar
      quickQuestions: [
        "Example question 1?",
        "Example question 2?",
        "Example question 3?",
      ],
    },
  },
};

const inkeepWidget = Inkeep().embed(config);
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your VuePress docs



<Snippet file="vuepress/QuickStartVuePress.mdx" />

Now, configure the chat button component.

```js addInkeep.js
const initializeInkeep = () => {
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-color-mode");
          return currentTheme === "dark";
        },
        colorModeAttribute: "data-color-mode",
      },
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", addInkeepWidget);

    return;
  }

  addInkeepWidget();
};
```

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add an Embedded Chat to your VuePress docs

Add AI chat to your VuePress docs.

<Snippet file="vuepress/QuickStartVuePress.mdx" />

## Create container for embedded chat

To create a container for the embedded chat component, navigate to your desired page and add the following code:

```jsx
<ClientOnly>
  <div id="inkeepEmbeddedChat"></div>
</ClientOnly>
```

### Configure the component

Next, configure the Inkeep widget:

```js addInkeep.js
const initializeInkeep = () => {
  const addInkeepWidget = () => {
    const embeddedContainer = document.querySelector("#inkeepEmbeddedChat");

    if (!embeddedContainer) return;

    const inkeepWiddget = Inkeep().embed({
      componentType: "EmbeddedChat",
      targetElement: "#inkeepEmbeddedChat",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-md-color-scheme");
          return currentTheme === "dracula" || currentTheme === "dark";
        },
        colorModeAttribute: "data-md-color-scheme",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF",
          organizationDisplayName: "Inkeep",
        },
      },
    });
  };

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", addInkeepWidget);

    return;
  }

  addInkeepWidget();
};
```

<Snippet file="learn_more/EmbeddedChatSettingReact.mdx" />


# Add AI Search to your VuePress docs



<Snippet file="vuepress/QuickStartVuePress.mdx" />

## Create container for search bar

To create a container for the search bar component, navigate to your desired page and add the following code:

```jsx
<ClientOnly>
  <div id="inkeepSearchBar"></div>
</ClientOnly>
```

### Configure the component

Next, configure the Inkeep widget:

```js addInkeep.js
const initializeInkeep = () => {
  const addInkeepWidget = () => {
    const searchContainer = document.querySelector("#inkeepSearchBar");

    if (!searchContainer) return;

    const inkeepWiddget = Inkeep().embed({
      componentType: "SearchBar",
      targetElement: "#inkeepSearchBar",
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          const currentTheme = el.getAttribute("data-md-color-scheme");
          return currentTheme === "dracula" || currentTheme === "dark";
        },
        colorModeAttribute: "data-md-color-scheme",
      },
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF",
          organizationDisplayName: "Inkeep",
        },
      },
    });
  };

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", addInkeepWidget);

    return;
  }

  addInkeepWidget();
};
```

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Webflow site



<Snippet file="webflow/QuickStartWebflow.mdx" />

## Initialize the widget

Click on the **Footer code** section and add the below script.

<Snippet file="InkeepChatButtonDefineJS.mdx" />

Press **Save** to apply your settings.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add a Custom Trigger to your Webflow site



<Snippet file="webflow/QuickStartWebflow.mdx" />

## Create a trigger

You need to create a trigger in your application with ID - `inkeepButton`. This can be done using Webflow's built-in **designer**.

Example:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-trigger-example.png" />

## Initialize the widget

Click on the **Footer code** section and add the below script.

```html
<script type="text/javascript" defer>
  const inkeepButton = document.getElementById("inkeepButton");
  const inkeepDiv = document.createElement("div");
  
  inkeepDiv.id = "inkeepModal";
  inkeepDiv.style.position = "absolute";
  document.body.appendChild(inkeepDiv);

  const handleClose = () => {
    inkeepWidget.render({
      ...config,
      isOpen: false,
    });
  };

  const handleOpen = () => {
    inkeepWidget.render({
      ...config,
      isOpen: true,
    });
  }

  // configure and initialize the widget
  const config = {
    componentType: "CustomTrigger", // required
    targetElement: inkeepDiv, // required
    properties: {
      isOpen: false, // required
      onClose: handleClose, // required
      onOpen: undefined,
      baseSettings: {
        apiKey: "INKEEP_API_KEY", // required
        integrationId: "INKEEP_INTEGRATION_ID", // required
        organizationId: "INKEEP_ORGANIZATION_ID", // required
        primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
        organizationDisplayName: "Inkeep",
        // ...optional settings
      },
      modalSettings: {
        // optional settings
      },
      searchSettings: {
        // optional settings
      },
      aiChatSettings: {
        // optional settings
        botAvatarSrcUrl: "/img/inkeep-logo.svg",
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  };

  // Embed the widget using the `Inkeep.embed()` function.
  const inkeepWidget = Inkeep().embed(config);

  inkeepButton.addEventListener("click", handleOpen);
</script>
```

<Snippet file="learn_more/CustomTriggerSettingsJS.mdx" />


# Add an Embedded AI Chat to your Webflow site



<Snippet file="webflow/QuickStartWebflow.mdx" />

## Create a new page

Next, you need to create a new page for the chat widget. You can do this using the built-in Webflow **designer**.

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-create-new-page.png" />

### Add a container to the page

Select **Container** from the list of elements and add it to the page:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-create-chat-container.png" />

### Set the container ID

Set the ID of the container to `inkeepEmbeddedChat`:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-set-chat-container-ID.png" />

## Configure the widget

Go back to **Site settings** and navigate to **Custom code**. Add the below in the **Footer code** section.

<Snippet file="InkeepEmbeddedChatDefineJS.mdx" />

Press **Save** to apply your settings.

As a result, a chat widget will be added to the Ask AI page:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-chat-example.png" />

<Snippet file="learn_more/CommonSettings.mdx" />


# Add AI Search to your Webflow site



<Snippet file="webflow/QuickStartWebflow.mdx" />

## Creating the container

Next, you need to add a container for the widget. You can do this using the built-in Webflow **designer**.

### Add the placeholder

Add a container to your navigation bar where the search bar will be placed:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-create-widget-container.png" />

### Set the container ID

Set the ID of the container to `inkeepSearchBar`:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/webflow/webflow-set-widget-container-ID.png" />

## Configure the widget

Go back to **Site settings** and navigate to **Custom code**. Add the below in the **Footer code** section.

```html
<script type="text/javascript" defer>
  const widgetContainer = document.getElementById("inkeepSearchBar");

  const addInkeepWidget = function () {
    const inkeepScript = document.getElementById("inkeep-script");

    inkeepScript.addEventListener("load", () => {
      const inkeepWidget = Inkeep().embed({
        componentType: "SearchBar",
        targetElement: widgetContainer,
        properties: {
          baseSettings: {
            apiKey: "INKEEP_API_KEY", // required
            integrationId: "INKEEP_INTEGRATION_ID", // required
            organizationId: "INKEEP_ORGANIZATION_ID", // required
            primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
            organizationDisplayName: "Inkeep",
            // ...optional settings
            theme: {
              // stylesheetUrls: ['/path/to/stylesheets'], // optional
              // ...optional settings
            },
          },
          modalSettings: {
            // optional settings
          },
          searchSettings: {
            // optional settings
          },
          aiChatSettings: {
            // optional settings
            botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
            quickQuestions: [
              "Example question 1?",
              "Example question 2?",
              "Example question 3?",
            ],
          },
        },
      });
    });
  };

  addInkeepWidget(); // initialize the widget
</script>
```

Press **Save** to apply your settings.

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your WordPress site



<Snippet file="wordpress/QuickStartWordPress.mdx" />

## Initialize the widget

Next, under the **Tools** tab open **Head & Footer Code** option. Add the code below to the **HEAD Code** section:

```html
<script
  id="inkeep-script"
  src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
  type="module"
  defer
></script>

<script defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "ChatButton",
      properties: {
        chatButtonType: "PILL",
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) addInkeepWidget(); // initialize the widget
  });
</script>
```

## Save custom code

Click on the **Save Changes**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search to your WordPress site



<Snippet file="wordpress/QuickStartWordPress.mdx" />

## Creating the container

Add a container for the widget. You can do this using the built-in WordPress functionality.

1.  Open the **Appearance** tab
2.  Select **Editor** mode
3.  Select the area to which you want to add the **SearchBar** widget
4.  Add a **Custom HTML** block

Example:

<img className="w-full block" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/wordpress/wordpress-search-bar-container.png" />

## Initialize the widget

Next, under **Tools** go to the **Head & Footer Code** subsection. Add the code below to the **HEAD Code** section:

```html
<script
  id="inkeep-script"
  src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
  type="module"
  defer
></script>

<script defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = () => {
    const inkeepWidget = Inkeep().embed({
      componentType: "SearchBar",
      targetElement: document.getElementById("inkeepSearchBar"),
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
          theme: {
            // stylesheetUrls: ['/path/to/stylesheets'], // optional
            // ...optional settings
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) addInkeepWidget(); // initialize the widget
  });
</script>
```

## Save custom code

Click on the **Save Changes**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# Add AI Chat to your Zendesk help center



<Snippet file="zendesk/QuickStartZendesk.mdx" />

## Initialize the widget

Next, in the same file, paste the following code:

<Snippet file="InkeepChatButtonDefineJS.mdx" />

## Save custom code

Click on the **Publish**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/ChatButtonSettingsJS.mdx" />


# Add AI Search & Chat to your Zendesk help center



<Snippet file="zendesk/QuickStartZendesk.mdx" />

## Creating the container

Add a container for the widget:

1.  Open the `home_page.hbs` and `section_page.hbs` files
2.  Add an element with the **ID** - `inkeepSearchBarNav` and `inkeepSearchBarHero`
3.  Apply the below **before** -> **after** changes to `home_page.hbs`:

<CodeGroup>
  ```html before
  <!-- other code -->
  <section id="main-content" class="section hero">
    <div class="hero-inner">
      <h2 class="visibility-hidden">{{ t 'search' }}</h2>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="12"
        height="12"
        focusable="false"
        viewBox="0 0 12 12"
        class="search-icon"
        aria-hidden="true"
      >
        <circle cx="4.5" cy="4.5" r="4" fill="none" stroke="currentColor" />
        <path stroke="currentColor" stroke-linecap="round" d="M11 11L7.5 7.5" />
      </svg>
      {{search submit=false instant=settings.instant_search class='search
      search-full'}}
    </div>
  </section>
  ```

  ```html after
  <!-- other code -->
  <section id="main-content" class="section hero">
    <div class="hero-inner">
      <div id="inkeepSearchBarHero"></div>
    </div>
  </section>
  ```
</CodeGroup>

4.  Apply the below **before** -> **after** changes to `section_page.hbs`:

<CodeGroup>
  ```html before
  <!-- other code -->
  <div class="sub-nav">
    {{breadcrumbs}}
    <div class="search-container">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="12"
        height="12"
        focusable="false"
        viewBox="0 0 12 12"
        class="search-icon"
        aria-hidden="true"
      >
        <circle cx="4.5" cy="4.5" r="4" fill="none" stroke="currentColor" />
        <path stroke="currentColor" stroke-linecap="round" d="M11 11L7.5 7.5" />
      </svg>
      {{search scoped=settings.scoped_kb_search submit=false}}
    </div>
  </div>
  ```

  ```html after
  <!-- other code -->
  <div class="sub-nav">
    {{breadcrumbs}}
    <div class="search-container">
      <div id="inkeepSearchBarNav"></div>
    </div>
  </div>
  ```
</CodeGroup>

5.  Click **Publish**

## Custom CSS

Zendesk allows you to add a custom css file to the **assets** directory:

1.  Create an `inkeep-styles.css` stylesheet and paste in the below::

```css inkeep-styles.css
[data-theme="light"] .ikp-search-bar-trigger__wrapper {
  background: white;
  border-radius: 6px;
  width: 100%;
}
```

2.  In the theme code editor, click **Add**
3.  Click on **Asset**
4.  Upload the created css file

## Initialize the widgets

Next, go back to the `header.hbs` file and add the following code at the end of the file:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = (componentType, targetElementId) => {
    const inkeepStyles = "{{asset 'inkeep-styles.css'}}"; // <--- should match the uploaded file
    const inkeepWidget = Inkeep().embed({
      componentType,
      targetElement: targetElementId,
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
          theme: {
            stylesheetUrls: [inkeepStyles], // optional
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) {
      const navSearchBarContainer =
        document.getElementById("inkeepSearchBarNav");
      const heroSearchBarContainer = document.getElementById(
        "inkeepSearchBarHero"
      );

      addInkeepWidget("ChatButton"); // initialize the ChatButton widget
      navSearchBarContainer &&
        addInkeepWidget("SearchBar", "#inkeepSearchBarNav"); // initialize the nav SearchBar widget
      heroSearchBarContainer &&
        addInkeepWidget("SearchBar", "#inkeepSearchBarHero"); // initialize the hero SearchBar widget
    }
  });
</script>
```

## Different styles between hero and nav bar

You can conditionally apply different stylesheets to the navigation bar or hero search bar.

```js
// ...

const inkeepSearchBarNavStyles = "{{asset 'inkeep-styles.css'}}"; // <--- should match the uploaded file
const inkeepSearchBarHeroStyles = "{{asset 'inkeep-hero-styles.css'}}"; // <--- should match the uploaded file

const stylesheetUrls =
  componentType === "ChatButton"
    ? []
    : targetElementId === "inkeepSearchBarHero"
    ? [inkeepSearchBarHeroStyles]
    : [inkeepSearchBarNavStyles];

const inkeepWidget = Inkeep().embed({
  componentType,
  targetElement: targetElementId,
  properties: {
    // ...other settings,
    baseSettings: {
      // ...other baseSettings,
      theme: {
        stylesheetUrls,
      },
    },
  },
});
// ...
```

## Save custom code

Click on the **Publish**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/CommonSettings.mdx" />


# Embedded Chat

Add AI chat to your Zendesk Help Center.

<Snippet file="zendesk/QuickStartZendesk.mdx" />

## Creating a custom page

1.  Click on **Add**

2.  Click on **Custom page**

3.  Specify a name for the custom page - `ask-ai`

4.  Click on **Add custom page**

5.  Add the code below to a new file (`ask_ai.hbs`):

```html ask_ai.hbs
<div class="container">
  <div class="category-container">
    <div class="category-content">
      <header class="page-header">
        <h1>Ask AI ✨</h1>
      </header>
      <div id="main-content" class="section-tree">
        <div id="inkeepEmbeddedChat"></div>
      </div>
    </div>
  </div>
</div>
```

## Custom CSS (optional)

Zendesk allows you to add a custom css file to the **assets** directory:

1.  Create an `inkeep-styles.css` stylesheet and paste in the below:

```css inkeep-styles.css
[data-theme='light'] .ikp-search-bar-trigger__wrapper {
  background: white;
  border-radius: 6px;
  width: 100%;
}
```

2.  In the theme code editor, click **Add**
3.  Click on **Asset**
4.  Upload the created css file

## Initialize the widget

Next, go back to the `header.hbs` file and add the following code at the end of the file:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = (targetElementId) => {
    const inkeepStyles = "{{asset 'inkeep-styles.css'}}" // <--- replace with provide template string (if different)
    const inkeepWidget = Inkeep().embed({
      componentType: "EmbeddedChat",
      targetElement: targetElementId,
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings,
          theme: {
            stylesheetUrls: [inkeepStyles], // optional (remove if not using custom styles)
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) {
      const chatContainer = document.getElementById('inkeepEmbeddedChat');
      chatContainer && addInkeepWidget("#inkeepEmbeddedChat"); // initialize the EmbeddedChat widget
    }
  });
</script>
```

## Save custom code

Click on the **Publish**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/EmbeddedChatSettingsJS.mdx" />


# Add AI Search to your Zendesk help center



<Snippet file="zendesk/QuickStartZendesk.mdx" />

## Creating the container

Add a container for the widget:

1.  Open the `home_page.hbs` and `section_page.hbs` files
2.  Add an element with the **ID** - `inkeepSearchBarNav` and `inkeepSearchBarHero`
3.  Apply the below **before** -> **after** changes to `home_page.hbs`:

<CodeGroup>
  ```html before
  <!-- other code -->
  <section id="main-content" class="section hero">
    <div class="hero-inner">
      <h2 class="visibility-hidden">{{ t 'search' }}</h2>
      <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" focusable="false" viewBox="0 0 12 12" class="search-icon" aria-hidden="true">
        <circle cx="4.5" cy="4.5" r="4" fill="none" stroke="currentColor"/>
        <path stroke="currentColor" stroke-linecap="round" d="M11 11L7.5 7.5"/>
      </svg>
      {{search submit=false instant=settings.instant_search class='search search-full'}}
    </div>
  </section>
  ```

  ```html after
  <!-- other code -->
  <section id="main-content" class="section hero">
    <div class="hero-inner">
  	  <div id="inkeepSearchBarHero"></div>
    </div>
  </section>
  ```
</CodeGroup>

4.  Apply the below **before** -> **after** changes to `section_page.hbs`:

<CodeGroup>
  ```html before
  <!-- other code -->
  <div class="sub-nav">
    {{breadcrumbs}}
    <div class="search-container">
    <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" focusable="false" viewBox="0 0 12 12" class="search-icon" aria-hidden="true">
      <circle cx="4.5" cy="4.5" r="4" fill="none" stroke="currentColor"/>
      <path stroke="currentColor" stroke-linecap="round" d="M11 11L7.5 7.5"/>
    </svg>
    {{search scoped=settings.scoped_kb_search submit=false}}
  </div>
  ```

  ```html after
  <!-- other code -->
  <div class="sub-nav">
    {{breadcrumbs}}
    <div class="search-container">
      <div id="inkeepSearchBarNav"></div>
    </div>
  </div>
  ```
</CodeGroup>

5.  Click **Publish**

## Custom CSS

Zendesk allows you to add a custom css file to the **assets** directory:

1.  Create an `inkeep-styles.css` stylesheet and paste in the below::

```css inkeep-styles.css
[data-theme='light'] .ikp-search-bar-trigger__wrapper {
  background: white;
  border-radius: 6px;
  width: 100%;
}
```

2.  In the theme code editor, click **Add**
3.  Click on **Asset**
4.  Upload the created css file

## Initialize the widget

Next, go back to the `header.hbs` file and add the following code at the end of the file:

```html
<script type="text/javascript" defer>
  const inkeepScript = document.getElementById("inkeep-script");

  // configure and initialize the widget
  const addInkeepWidget = (targetElementId) => {
    const inkeepStyles = "{{asset 'inkeep-styles.css'}}" // <--- replace with provide template string (if different)
    const inkeepWidget = Inkeep().embed({
      componentType: "SearchBar",
      targetElement: targetElementId,
      properties: {
        baseSettings: {
          apiKey: "INKEEP_API_KEY", // required
          integrationId: "INKEEP_INTEGRATION_ID", // required
          organizationId: "INKEEP_ORGANIZATION_ID", // required
          primaryBrandColor: "#26D6FF", // your brand color, widget color scheme is derived from this
          organizationDisplayName: "Inkeep",
          // ...optional settings
          theme: {
            stylesheetUrls: [inkeepStyles], // optional
          },
        },
        modalSettings: {
          // optional settings
        },
        searchSettings: {
          // optional settings
        },
        aiChatSettings: {
          // optional settings
          botAvatarSrcUrl: "https://mydomain.com/mylogo", // use your own bot avatar
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
        },
      },
    });
  };

  inkeepScript.addEventListener("load", () => {
    if (document.body) {
      const navSearchBarContainer = document.getElementById('inkeepSearchBarNav');
  		const heroSearchBarContainer = document.getElementById('inkeepSearchBarHero');

      navSearchBarContainer && addInkeepWidget("#inkeepSearchBarNav"); // initialize the nav SearchBar widget
      heroSearchBarContainer && addInkeepWidget("#inkeepSearchBarHero"); // initialize the hero SearchBar widget
    }
  });
</script>
```

## Different styles between hero and nav bar

You can conditionally apply different stylesheets to the navigation bar or hero search bar.

```js
// ...

const inkeepSearchBarNavStyles = "{{asset 'inkeep-styles.css'}}" // <--- should match the uploaded file
const inkeepSearchBarHeroStyles = "{{asset 'inkeep-hero-styles.css'}}" // <--- should match the uploaded file

const stylesheetUrls = targetElementId === 'inkeepSearchBarHero' ? [inkeepSearchBarHeroStyles] : [inkeepSearchBarNavStyles];

const inkeepWidget = Inkeep().embed({
  componentType: "SearchBar",
  targetElement: targetElementId,
  properties: {
      // ...other settings
      baseSettings: {
        // ...other baseSettings,
        theme: {
          stylesheetUrls,
        },
      },
    }
  });
// ...
```

## Save custom code

Click on the **Publish**. Navigate to your web application to make sure the widget is installed correctly.

<Snippet file="learn_more/SearchBarSettingsJS.mdx" />


# AI for Customers



Use AI to help your users find what they need, get scenario-specific guidance, and have access to 24/7 troubleshooting help.

## Ingest your content

Ingest all of your customer-facing content with our out-of-box connectors and scrapers.

**Supported Sources**

*   Technical docs (Docusaurus, ReadMe, GitBook, Mintlify, etc.)
*   Help center and FAQs
*   Website and blogs
*   GitHub
*   Community threads (Slack, Discord, and Discourse)
*   Open API and GraphQL schemas
*   PDF, Markdown, CSV, and other files
*   YouTube & other video sources
*   Knowledge Bases (Notion, Confluence, Sharepoint, etc.)

## Add to your community

Create an `✨ask-ai` channel for your Discord or Slack community.

<CardGroup cols={2}>
  <Card title="Slack" icon="slack" color="#26D6FF" href="/integrations/slack">
    Add to a Slack workspace.
  </Card>

  <Card title="Discord" icon="discord" color="#26D6FF" href="/integrations/discord">
    Add to a Discord server.
  </Card>
</CardGroup>

## Add to your site

Add Inkeep to your docs, marketing site, help center, or app. Check out our [Showcase](https://inkeep.com/showcase) for examples or follow a quickstart:

*   [Docusaurus](/integrations/docusaurus/chat-button)
*   [GitBook](/integrations/gitbook/chat-button)
*   [ReadMe](/integrations/readme/chat-button)
*   [Fumadocs](/integrations/fumadocs/chat-button)
*   [Next.js](/integrations/nextjs/chat-button)
*   [Zendesk](/integrations/zendesk/help-center/chat-button)
*   [Webflow](/integrations/webflow/chat-button)
*   [Discourse](/integrations/discourse)
*   [Framer](/integrations/framer/chat-button)
*   [WordPress](/integrations/wordpress/chat-button)
*   [Astro](/integrations/astro/chat-button)
*   [Gatsby](/integrations/gatsby/chat-button)
*   [Remix](/integrations/remix/chat-button)
*   [Nextra](/integrations/nextra/chat-button)
*   [Mintlify](/integrations/mintlify)
*   [VitePress](/integrations/vitepress/chat-button)
*   [VuePress](/integrations/vuepress/chat-button)
*   [Document360](/integrations/document360/chat-button)
*   [Redocly](/integrations/redocly/api-docs/chat-button)
*   [Sphinx](/integrations/sphinx/chat-button)
*   [Hugo](/integrations/hugo/ananke)
*   [Bettermode](/integrations/bettermode)
*   [MkDocs](/integrations/mkdocs/chat-button)
*   [Google Tag Manager](/integrations/google-tag-manager/chat-button)

To customize the UI components or add Inkeep to any platform, checkout the `uikit` [docs](/ui-components/overview).

## Monitor

Use our [dashboard](https://portal.inkeep.com) to monitor usage and close the feedback loop.

<CardGroup cols={2}>
  <Card title="Usage Analytics" icon="chart-mixed" color="#26D6FF" href="/analytics/reports#chat-sessions">
    View all questions asked and track thumbs up, down, and other stats.
  </Card>

  <Card title="AI Reports" icon="file-chart-column" color="#08C9F7" href="/analytics/reports#insights-ai-reports">
    Pinpoint gaps in your documentation and monitor trending topics.
  </Card>
</CardGroup>

## Advanced (Tools & APIs)

Use our AI API to create copilots that can take product-specific actions ("tools") or be used in automation tasks like form-filling ("structured outputs") with context about your product.

<CardGroup cols={2}>
  <Card title="Context API" icon="code" color="#26D6FF" href="/ai-api/chat-completions-api#context-mode">
    OpenAI-compatible Chat Completion API.
  </Card>

  <Card title="Quickstarts" icon="code" color="#26D6FF" href="https://github.com/inkeep/inkeep-chat-api-examples">
    For Vercel AI SDK and OpenAI SDKs.
  </Card>
</CardGroup>

## Get started

Ready to use AI to improve your self-serve search and support experiences? [Try Inkeep on your content](https://inkeep.com/demo) or [Schedule a call](https://inkeep.com/schedule-demo).


# AI for Support Teams



All the AI tools your support team needs to deflect questions and help your team provide fast support when needed.

## Tools for your team

### Support Copilot

Keep, our copilot for support teams, is a conversational AI sidekick that helps your team resolve tickets quickly.

Keep intelligently provides:

*   Draft answers
*   Relevant Sources
*   Summaries
*   To-dos

...and other relevant suggestions based on the context of a ticket.

Get started:

<CardGroup cols={2}>
  <Card title="Zendesk Sidebar" icon="user-headset" color="#26D6FF" href="/support-tools/agent-copilot/zendesk">
    Install as a plugin for Zendesk.
  </Card>

  <Card title="Any platform" icon="sidebar-flip" color="#26D6FF" href="/support-tools/agent-copilot/chrome-extension">
    Use the Chrome Extension with any support platform.
  </Card>
</CardGroup>

### Slack Bot

Add the Inkeep Slack bot to your `#support-triage` or other internal channels where your team collaborates on customer questions.

Use Inkeep with:

<CardGroup cols={2}>
  <Card title="Loop-In Mode" icon="at" color="#26D6FF" href="/integrations/slack#loop-in-mode">
    Tag Inkeep in an existing thread only when relevant.
  </Card>

  <Card title="Auto-Reply Mode" icon="robot" color="#26D6FF" href="/integrations/slack#auto-reply-mode">
    Have Inkeep automatically give suggestions on any new thread.
  </Card>
</CardGroup>

### Private Sources

Your internal-facing assistant can leverage private sources your customer-facing assistant can't.

This includes:

*   Support tickets
*   Slack conversations
*   Internal KBs (Notion, Confluence, Google Drive, Sharepoint, etc.)

Use our out-of-box connectors to tap into any of these sources.

## Ticket Deflection

### AI Assistant

Use Inkeep's [customer-facing integrations](/overview/ai-for-customers) to embed AI self-service support anywhere users have questions: docs, help center, community, in-app and more.

### AI Support Forms

Inkeep AI knows when it can answer and when it needs to hand off to humans. Incorporate into the ticket creation process to deflect questions.

<CardGroup cols={2}>
  <Card title="Standalone Form" icon="list-check" color="#26D6FF" href="https://github.com/inkeep/intelligent-support-form">
    Provide an AI answer if confident and otherwise smartly categorize a new ticket.
  </Card>

  <Card title="Within the AI assistant" icon="message-bot" color="#26D6FF" href="/customization-guides/embedded-support-form">
    Embed into the AI chat so a user can seamlessly escalate to human support.
  </Card>
</CardGroup>

### Auto-reply & Automations

Auto-reply to new support tickets, emails, GitHub issues, or forum posts only when confident using our APIs or integrations.

<CardGroup cols={2}>
  <Card title="Auto-reply to Tickets" icon="ticket" color="#26D6FF" href="/support-tools/auto-reply/tickets">
    Automatically respond to support tickets with AI-powered answers when confident.
  </Card>

  <Card title="Auto-reply to Live Chat" icon="comments" color="#26D6FF" href="/support-tools/auto-reply/live-chat">
    Have an AI agent handle livechats and handoff to your team when needed.
  </Card>

  <Card title="Question-Answer API" icon="sparkles" color="#26D6FF" href="/ai-api/chat-completions-api#question-answer-mode">
    Use and customize our AI responses to auto-reply to any user question in any channel.
  </Card>

  <Card title="Context API" icon="code" color="#26D6FF" href="/ai-api/chat-completions-api#context-mode">
    Classify tickets, pre-fill forms, and use JSON outputs to automate any support workflow.
  </Card>
</CardGroup>

## Close the Content Loop

Continuously improve your knowledge base and support content.

<CardGroup cols={2}>
  <Card title="Turn Tickets into FAQs" icon="clipboard-list-check" color="#26D6FF" href="/support-tools/agent-copilot/draft-faq">
    Automatically reformat closed tickets into private or public FAQs.
  </Card>

  <Card title="AI Reports" icon="chart-line" color="#26D6FF" href="/analytics/reports">
    Get insights on content gaps and trending topics to improve your documentation.
  </Card>
</CardGroup>

## Measure Impact

Track the effectiveness of your AI-powered support tools.

<CardGroup cols={2}>
  <Card title="Analytics" icon="chart-mixed" color="#26D6FF" href="/analytics/reports">
    Monitor key metrics like percentage of questions with a confident answer and thumbs up/down ratios.
  </Card>

  <Card title="Add to your own BI" icon="chart-network" color="#26D6FF" href="/customization-guides/use-your-own-analytics">
    Log chat events to your own analytics tools to measure impact on support and product activation funnels.
  </Card>
</CardGroup>

## Get started

Ready to use AI your support team and users actually love? [Schedule a call](https://inkeep.com/schedule-demo).


# Privacy

How Inkeep protects your data.

## Overview

We know that privacy and security is critical for many organizations. Inkeep is committed to protecting your organization's and user's data and privacy.

We use industry-standard security practices, use reputable subprocessors with SOC 2 compliance, and provide the controls needed for organizations to meet their own data protection requirements.

We actively follow security best practices like undergoing regular external security reviews and penetration tests. Inkeep is **SOC 2 Type II compliant**.

<Note>
  This article is only an overview of Inkeep's approach to data privacy, not a legal agreement.
  See our privacy policy [here](https://inkeep.com/policies/privacy) and please reach out to <a href="mailto:privacy@inkeep.com?subject=Inkeep Privacy Question">[privacy@inkeep.com](mailto:privacy@inkeep.com)</a> for any other documentation requests or questions.
</Note>

## Use of LLMs

To provide our search and chat services, we use foundational large language models (LLMs) and artificial intelligence (AI) services from the following providers:

*   [Anthropic](https://legal.anthropic.com)

*   [Azure OpenAI](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/data-privacy)

*   [OpenAI](https://openai.com/policies/api-data-usage-policies)

<Note>
  Our providers will *never* use your data to train
  AI or LLM models. See the links above for details of each provider's policies.
  While Inkeep may train AI or LLM models for use by your organization, for example by fine-tuning an LLM model specific to your product, we'll never expose your or your user's data to unauthorized third parties.
</Note>

## Categories of Data Subjects

Information we collect can be in relation to the following types of **Users**:

*   **End Users** - Your end users or customers.
*   **Employees** - Your employees.

## Categories of Collected Data

### Knowledge Base Documents

**Knowledge Base Documents** include information provided to Inkeep for use in our search and chat service for your organization. These can include:

*   Technical documentation
*   Website and blogs
*   Sources that include end-user generated content like StackOverflow, GitHub, Discourse, Slack and Discord
*   Support desk tickets and FAQs
*   Internal documents
*   and others

You can control what content is provided to Inkeep for ingestion into your knowledge base. You can view and delete this content at any time.

### Prompts and Responses

*   **Prompts** - The text or input provided by a user to Inkeep's search or chat service. Prompts may also include context information provided by your organization.
*   **Responses** - The text or output provided in response to a prompt by Inkeep's search or chat service.

Prompts and Responses are retained for the purposes of:

1.  **Usage Analytics** - Providing Employees with topical, sentiment, and other analytics of Prompts and Responses.
2.  **End User Functionality** - Providing functionality to End Users like thumbs-up and thumbs-down feedback, "Share chat", and conversation history.
3.  **Abuse and Misuse Monitoring**
4.  **Service Improvements** - Monitoring and improving the quality of Inkeep's search and chat service.

Enterprise Customers can customize the below:

*   Retention period of conversations
*   Enabling or disabling of Usage Analytics or End User Functionality

Prompts and Response pairs are only associated with User Metadata if that is collected. See the next section.

### User Metadata

**User Metadata** can include identifying information like IP address or browser session information of Users. Your applications can choose to provide user IDs, user segmentation, or other data that you'd like to include in Inkeep's Usage Analytics features.

<Note>
  You can customize and opt out of the collection of User Metadata to align with
  your own policies and preferences. See
  [here](/ui-components/common-settings/base) for information on how to
  configure this in Inkeep's web widgets. With these settings, you are able to
  control whether User Metadata is associated with Prompts and Responses or
  Application Information.
</Note>

### Application Information

We collect **Application Information**, which includes usage data generated in the process of providing Inkeep's services, to help troubleshoot, measure, and improve the performance and availability of our services.

## Advanced controls

For Enterprise customers with strict data protection requirements, we offer advanced data and privacy controls. These can include:

*   Zero-retention policy for any user prompts and responses.
*   Use of data de-identification services, like Google Cloud Platform's [Sensitive Data Protection](https://cloud.google.com/dlp/docs), for processing Knowledge Base Documents or Prompts and Responses.
*   Sending of user prompts to specific LLM providers.
*   Using custom cloud infrastructure and vector database providers providers for storage of data.
*   Role-based access control on which employees can view Prompts and Responses or User Metadata.
*   Restricting user access to authenticated users.
*   A HIPAA BAA agreement with certain data processing policies.

In general, we will work with your legal and security teams to work with your requirements. We will review with them our service agreement, data processing agreement, security policies, and other relevant documents.

## Contacting us

If you have a data request or questions about our use of data and processing, please reach out to <a href="mailto:privacy@inkeep.com?subject=Inkeep Privacy Question">[privacy@inkeep.com](mailto:privacy@inkeep.com)</a>.

If you are aware of an information security incident, unauthorized access, policy violation, security weakness, or suspicious activity related to Inkeep, please report it by sending an email with any relevant details to
<a href="mailto:incidents@inkeep.com">[incidents@inkeep.com](mailto:incidents@inkeep.com)</a>.


# Why Inkeep?



## TL;DR

In short: quality.

Our #1 goal to help our customers ship AI self-serve experiences to their users with confidence.

For us, quality means developing our AI solution to:

*   Admit when it doesn't know and intellegently guide users to support channels
*   Consistently find the right content and not hallucinate
*   Provide rich citations that help users inspect answers
*   Leverage many sources while prioritizing authorative content

We also care deeply about end-user experiences, zero-effort onboarding, and actionable reporting for product teams. We built our product and team to deliver best-in-class experiences in each of these.

That said - don't take our word for it! Use our [free trial](https://inkeep.com/demo-form) to test Inkeep with your toughest questions.

## Hands-on Support

Quality for us also means working closely with any team we work to accomplish their goals. Consider us a partner who will work with you every step of the way.

<CardGroup cols={2}>
  <Card title="Contact us any time" icon="life-ring" color="#26D6FF" href="mailto:support@inkeep.com?subject=Get%20Help">
    Reach us over email, Slack, Discord, or Teams.
  </Card>

  <Card title="Enterprise Support" icon="slack" color="#26D6FF" href="mailto:support@inkeep.com?subject=Onboard%20To%20Slack">
    Dedicated onboarding, support, and SLAs.
  </Card>
</CardGroup>

## Technical Deep Dive ✨

In our journey, we've talked to hundreds of companies who are eager to use generative AI to provide better self-serve experiences for their users. Many of them had experimented with creating their own LLM Q\&A apps or tried other services, but often didn't ship because they felt the quality and reliability weren't there.

### Ingesting content from many sources

Knowledge about technical products often lives in many places: documentation, GitHub, forums, Slack and Discord channels, blogs, StackOverflow, support systems, and elsewhere. Smartly ingesting all of this content, and keeping it up to date over time, quickly becomes the full-time task of a team of data engineers.

Inkeep addresses this by:

*   Automatically ingesting content from common public and private content sources with out-of-the-box integrations (while prioritizing them appropriately)

*   Frequently re-crawling your sources to find differences and keep your knowledge base up to date.

### Finding the most relevant content

[Retrieval augmented generation (RAG)](https://arxiv.org/abs/2005.11401) is the best way to use LLMs to answer questions regarding domain-specific content. At a high level, it involves taking a user question, finding the most relevant content, and feeding it to an LLM model.

RAG relies on finding the relevant documents and "chunks" within those documents needed to answer user questions. The problem is, popular ways of doing retrieval - like slicing up all content into `n`-character chunks - are often arbitrary and ineffective.

Retrieval becomes even more challenging as the number of documents and sources increase. More content can mean higher coverage of user questions, but often also means more noise and the need for a precise retrieval system.

Our retrieval and neural search engines address this by using:

*   Accounting for **time**, **author**, **source type**, **and other metadata** that's important for prioritizing trustworthy content.

*   **Custom embedding and chunking strategies** for each content source. The most effective embedding and chunking strategy for a Slack conversation is very different from one for a "How-to" article.

*   **Neural search** that combines semantic and keyword search to balance vector similarity and keyword matching.

*   **Tailoring of the embedding space** to your specific organization and content. Out-of-the-box embedding models don't account for what we call the "semantic space" of your company and your products. For example, "Retrieval system" is much closer in semantic meaning to "Feature" for Inkeep than for other companies.

*   ...and more.

If you're curious about our technical approach, join [our newsletter](https://inkeep.typeform.com/to/IgrltCbO?utm_source=why_inkeep_article) where we share product updates and engineering deep-dives.

### Minimizing hallucinations

Conversational large language models are trained to provide satisfying answers to users. Unfortunately, this makes them prone to providing answers that are unsubstantiated, i.e. "hallucinating". Dealing with hallucinations is notoriously difficult and a common blocker for many companies.

Here are some of the key ways in which we minimize hallucinations with our grounded-answer system:

1.  **Retrieving the right content** - When models are not given content that helps them answer a question, they are significantly more likely to hallucinate. That's in part why we focus so heavily on our search and retrieval engines.

2.  **Providing citations** - Citations give end-users easy ways to learn more and introspect answers. We include rich citations in our UI to make it easy to compare reference content and use citations to automatically evaluate, alert, and fix drift from source material.

3.  **Staying on topic** - We've implemented a variety of protections to keep model answers on topic. For example, the bot won't answer questions unrelated to your company and will guard against giving answers that create a poor perception of your product.

4.  **Rapidly experimenting at scale** - We continuously test and evaluate our entire retrieval and LLM-stack against both historical and new user questions. This allows us to identify and adopt new techniques while monitoring for regressions.

### Incorporating feedback

Even with a best-in-class retrieval and grounded-answer system, feedback loops are the key to continuous improvement of model performance over time.

Our platform has built-in mechanisms for this, including:

*   Thumbs up/down feedback from end-users

*   "Edit answer" feature for administrators

*   The ability to batch test a set of test questions

*   Custom FAQs

We also provide usage, topical, and sentiment analysis on all user questions. Product and content teams often use these insights to prioritize content creation and product improvements that address root causes of user questions.

### Production-ready service

To launch something confidently to end-users, it's essential to have:

*   High availability, geo-distributed, low latency search and chat services

*   API and UX monitoring

*   Continuous evaluation of search and chat results

Our platform already handles this at scale and answers hundreds of thousands of questions per month.

## The Team

Our [team](https://inkeep.com/team) is made up of engineers passionate about machine learning, data engineering, and user experiences. We're excited to solve the challenges in this space and help companies provide the best self-serve support and search experiences possible to their users.

We're fortunate to have the backing of reputable investors, including [Y Combinator](https://www.ycombinator.com/launches/IAP-inkeep-conversational-search-for-your-developer-product/edit) and [Khosla Ventures](https://www.khoslaventures.com/).


# UI Components Release Notes



## Release notes

### Version 0.3.18

*   Minor bug fixes

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.18
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js"
        integrity="sha384-L0dDhbtoDYVxDwHFUFUqgnSQioDzKMH4ylSDFya0pBHpbrSvvP/qybqTs2UEqpxa"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.17

*   Fix bug with modal closing unexpectedly in mobile view.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.17
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.17/dist/embed.js"
        integrity="sha384-Gg/fcaqv3nfqhVXHGKYAwuxqDKkrgseD/D3/TzCedGwVrf8xdZTNsunA7sqnftOE"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.16

*   Added `name` property to `get_help_option_clicked` event.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.16
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.16/dist/embed.js"
        integrity="sha384-w/YcUoMq/jYrvjQ7V0K4z5D93tgsj647CyCQosk44Q80hysCGkLySJQnvqFdh7ry"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.15

*   Added `pinToToolbar` prop on `GetHelpCallToAction`. See [here](/ui-components/common-settings/ai-chat#gethelpcalltoaction) for more details.
*   Minor bug fixes.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.15
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.15/dist/embed.js"
        integrity="sha384-eW0CsSnGiRITbNF2posLIgZ3yLpG3tLjur8uyQDOmgS93YLAhd+rSCoD1S72+qwV"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.14

*   Fix list style bug.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.14
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.14/dist/embed.js"
        integrity="sha384-KTJnPec5ksOwzBNqB/1/HdW55HRcQUFykelYQrOvbWSyFHF08y3oXRGYotfhYaGX"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.13

*   Added a prop `forceInitialDefaultView` to  `modalSettings` to allow modal to open in a certain view, regardless of which view was last opened. See [modalSettings](/ui-components/common-settings/modal#inkeepmodalsettings) for more details.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.13
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.13/dist/embed.js"
        integrity="sha384-EDSGAp6rhcaXKGssCgYgOWW6IUYvWRlDSQ1CO/8ApxDqwOVvTgjG/ac7lYDCG+zp"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.12

*   Fix export for `InkeepEvent` types.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.12
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.12/dist/embed.js"
        integrity="sha384-tS26d1BU5ihEP0oNwC7CvrUXH6Rmzvo1FivkwWOn09vukiqMtdLteeNZhtPxy7VW"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.11

*   Added optional `searchQueryParamKey` prop to `searchSettings`.
*   Added event logging for search result clicks.
*   Made the default placeholder text 'Search...' for small screens.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.11
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.11/dist/embed.js"
        integrity="sha384-eNRBPlulNL0ynUFNoot1+rMUpA5Aj3B47UfHxSGurhtkB4olxIAjyJoAdSpatwv9"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.10

*   Exposed the `SearchFunctions` in the embed js package.
*   Fixed `targetElement` being marked as required for `ChatButton` and `CustomTrigger` component types in the embed js package.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.10
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.10/dist/embed.js"
        integrity="sha384-VEutSDnJ8BuuVYkBRtyc4sNiJdghWHE+Ewkfp4dDqSqHP8q+eA+NGYL+WWIRn03c"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.9

*   Added a prop called `shouldShowCopyChatButton` to `aiChatSettings`, when enabled this will show a button to allow users to copy their entire conversation as markdown.
*   Added `messages` and `chatSessionId` as arguments to the `InvokeCallbackAction` for `GetHelpCallToAction` and `ShowEscalateToSupportButtonAction`.
*   Added analytics events for when a user clicks a get help menu item or support button.
*   Minor UI / bug fixes.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.9
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.9/dist/embed.js"
        integrity="sha384-tJ8HaC0dsMlX404eO80U7KVgBWkIjr84cuYCQDj9Bup+oiAV2CY95bNoVSQ/LsWo"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.8

*   Added `SELECT` field type to support form. See [here](/customization-guides/embedded-support-form#formfield) for details.
*   Added support for markdown strings in the `message` prop of the `SuccessConfirmationPageProps`. See [here](/customization-guides/embedded-support-form#successconfirmationpageprops) for details.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.8
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.8/dist/embed.js"
        integrity="sha384-3JVQvMaCNT/fi08RVecdp2dK0t0drimArxMVS7VeivoXNXPe2ghLq8DsIaPEHLuL"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.7

*   Small bug fix.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.7
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.7/dist/embed.js"
        integrity="sha384-/Rug8L+kHAeSGKz/aFPM7pfoxgU3UDiHSQEx0aMYVqUcbZaySQWpNiPRKMUguH9h"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.6

*   Small style fix.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.6
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.6/dist/embed.js"
        integrity="sha384-Vsa/UTFUSi59Qm7oPdRUj/ouG1iWGTUhk3c8Cf189xbKvjTIJBnAOVydZjVRRTSy"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.5

*   Update license field.
*   Small style tweak.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.5
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.5/dist/embed.js"
        integrity="sha384-YmgEvyTk3odDpz+yvuN0IjL27JY4slw4UTO56tZgHbcPYtf86O7KyfFftyj2DL2B"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.4

*   Minor UI improvements.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.4
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.4/dist/embed.js"
        integrity="sha384-h9Zfhmg7H4Z5TGyTXLKLSE/dQLIiHGsRw7EaIfxG5QQUaI0b9u7Li//0UcCPzyui"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.1

*   Minor UI improvements.

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.1
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.1/dist/embed.js"
        integrity="sha384-LWzgAnCjQxGCBq973/hByvj5dJe+s3CtBejGJpy60uETNFZzMYIOw7dJ0xMBdihj"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.3.0 `uikit`

*   We have renamed the `@inkeep/widgets` and `@inkeep/widgets-embed` packages to `@inkeep/uikit` and `@inkeep/uikit-js` respectively. Links to new packages can be found here: [uikit](https://www.npmjs.com/package/@inkeep/uikit) / [uikit-js](https://www.npmjs.com/package/@inkeep/uikit-js).
*   This new package contains a significant change to the search ui, moving from a two pane search result view to a single pane view with support for keyboard navigation.

<img className="w-full block dark:hidden border rounded-lg" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/release-notes/single-pane-search-light.png" />

<img className="w-full hidden dark:block rounded-lg" src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/release-notes/single-pane-search-dark.png" />

**Additions**

*   New prop in `searchSettings` `shouldShowAskAICard` to control whether or not to show the `Ask AI "<query>"` card in the search results. Default `true`.

#### Breaking changes

*   Renamed the following options for `chatButtonType`: `ICON_TEXT` is now `PILL` and `ICON_TEXT_SHORTCUT` is now `RECTANGLE_SHORTCUT`.
*   Renamed the type `InkeepWidgetBaseSettings` to `InkeepBaseSettings`.
*   Consolidated `chatButtonPillText` and `chatButtonCommandText` into a single prop `chatButtonText`.
*   Moved `stylesheets` and `stylesheetUrls` props into `theme` (within `baseSettings`).
*   `switchToChatMessage` is now `askAILabel` and will apply to both the `Ask AI "<query>"` card in the search results and to the toggle to switch to chat.
*   `submitOnEnterOnly` prop was removed from `searchSettings`.
*   Removed support for passing `colorMode` in the `theme` (which was deprecated in version `0.2.247`), `colorMode` will only be supported in `baseSettings`.
*   Removed support for passing `highlighterTheme` in `baseSettings` (which was deprecated in version `0.2.247`), `syntaxHighlighter` will only be supported in the `theme`.
*   Removed no longer used `CustomIcons`: `linkIndicator`, `newLine`, and `enter`.
*   Removed `shouldAnimateSwitchToChatButton` prop, the animation was removed since button was changed to a toggle.
*   Updated and removed some classnames to match ui changes.

    **Added**:

    *   `.ikp-ask-ai-card`
    *   `.ikp-search-result-line__first`
    *   `.ikp-search-result-line__second`

    **Removed**:

    *   `.ikp-switch-to-chat-button`
    *   `.ikp-switch-to-chat-button__shortcut`
    *   `.ikp-enter-icon`
    *   `.ikp-heading-tree__title`
    *   `.ikp-heading-tree__list`
    *   `.ikp-heading-tree__item`
    *   `.ikp-heading-tree__heading`
    *   `.ikp-new-line-icon`
    *   `.ikp-preview-content-header__heading`
    *   `.ikp-preview-content-header__title`
    *   `.ikp-link-indicator-icon`

<Tabs>
  <Tab title="npm">
    ```bash
      npm install @inkeep/uikit@0.3.0
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/uikit-js@0.3.0/dist/embed.js"
        integrity="sha384-8hZDIuOsAgmHWblZdK5Tz88TIZTdF7Wk24qbs4PMZIvo6GMXOZxPKwDo5HYEJQih"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.2.289

*   Update license.
*   Fix bug with duplicate titles in mobile view.

<Tabs>
  <Tab title="npm">
    ```bash
      npm i @inkeep/widgets@0.2.289
    ```
  </Tab>

  <Tab title="JS snippet">
    ```html
      <script
        type="module"
        src="https://unpkg.com/@inkeep/widgets-embed@0.2.289/dist/embed.js"
        integrity="sha384-z/gkVz89DKokF4p2d8pnApdsNAuy8LJ1+Qotg837yQ6TX2Tr31KfROcDgT2DMdoT"
        crossorigin="anonymous"
        defer
      >
      </script>
    ```
  </Tab>
</Tabs>

***

### Version 0.2.286

*   Allow users to pass in query params to append to documentation links. See `appendQueryParamsToUrls` in [baseSettings](/ui-components/common-settings/base) for more details.

***

### Version 0.2.285

*   Fix small style bug.

***

### Version 0.2.284

*   Allow users to pass in their own instance of Prism js to avoid conflicts in the global namespace.

***

### Version 0.2.282

*   Fix being able to style the switch to chat button with custom css.

***

### Version 0.2.281

*   Fixes to support for react 19.

***

### Version 0.2.280

*   Remove apollo dev tools console message.

***

### Version 0.2.279

*   Bug fix to improve multi language support.

***

### Version 0.2.278

*   Bug fixes.

***

### Version 0.2.276

*   Fallback to default bot avatar if user provided bot avatar doesn't load.
*   Improve styling for docs search preview.

***

### Version 0.2.275

*   Added a file input type to the support form.

***

### Version 0.2.274

*   Added a prop to change the chat button color. See [here](/ui-components/react/chat-button#properties) for more details.

***

### Version 0.2.272

*   Add workflowId to `chat_message_submitted` and `chat_message_bot_response_received` events.

***

### Version 0.2.271

*   Add info tooltip for workflow attachments.

***

### Version 0.2.270

*   Minor style tweak.

***

### Version 0.2.269

*   Exposed the `AIChatFunctions` in the embed js package.
*   Added `openForm` to `AIChatFunctions` so it can be triggered from outside the widget. See [here](/ui-components/common-settings/ai-chat#aichatfunctions) for more details.
*   Added `closeFormAction` to `FormConfig`. See [here](/customization-guides/embedded-support-form#formconfig) for more details.

#### Breaking changes

*   Removed `backToChatButtonLabel` from `SuccessConfirmationPageProps` and replaced with `SuccessConfirmationButton`. See [here](/customization-guides/embedded-support-form#successconfirmationbutton) for more details.

***

### Version 0.2.268

*   Add switch to chat and submit hot key (cmd + enter)
*   Fix bug with prefilled query not showing in search bar
*   Fix low chroma primary color derivatives calculations
*   Use filled sparkles for default icon in chat component

***

### Version 0.2.267

*   Adding additional class names for stylization.

***

### Version 0.2.266

*   Minor bug fixes

***

### Version 0.2.265

*   Edit default disclaimer copy.

***

### Version 0.2.264

*   Added support for `CustomIcons`. Refer to [custom icons](/customization-guides/style-components#custom-icons) for details.

***

### Version 0.2.263

*   Added support for a new `GetHelpCallToAction` and `ShowEscalateToSupportButtonAction`. Refer to [get help call to actions](/ui-components/common-settings/ai-chat#gethelpcalltoactioninvokecallback) and [support workflows](/customization-guides/embedded-support-form#invokecallbackaction) for details.
*   Fix for highlighting search text in dark mode.

***

### Version 0.2.262

*   Minor UI improvements.

***

### Version 0.2.261

*   Added support workflows feature. Refer to [Support workflows](/customization-guides/embedded-support-form) for details.
*   Minor UI improvements.

***

### Version 0.2.260

*   Minor bug fixes.

***

### Version 0.2.259

*   Adding `context` and `guidance` to `AIChatSettings` for providing dynamic information and instructions to the bot.

***

### Version 0.2.258

*   Fix copy button tooltip.
*   Add default targetElement for the `ChatButton` and `CustomTrigger` in the js snippet.
*   Add `colorModeSync` logic to the js snippet.

***

### Version 0.2.256

*   Fix to close modal if clicking on a search result link within the same page and `shouldOpenLinksInNewTab` is `false`.

***

### Version 0.2.254

*   Fix bug with loading stylesheets.

***

### Version 0.2.253

*   Deep merge properties in the `widgets-embed` package to improve devx.
*   Add fallback targetEl in the `widgets-embed` package.

***

### Version 0.2.252

*   Update default AI assistant avatar.

***

### Version 0.2.250

*   Add `data-state="active"` to the thumbs up and thumbs down buttons after the relative feedback has been submitted, by default they will have `data-state="inactive"`. This can be used to style the buttons differently after feedback submission.

***

### Version 0.2.249

*   Fix bug with chat button `fixedPositionXOffset` and `fixedPositionYOffset` props not being applied.
*   Add `shrink` size variant for the `SearchBarTrigger` component, this variant will shrink the search bar to just the search icon for the smallest screen sizes.

***

### Version 0.2.248

#### Breaking changes

*   Changed the prop `areOpenHotKeysDisabled` to `isShortcutKeyEnabled` in `modalSettings`
*   By default `isShortcutKeyEnabled` is `false` for all `InkeepChatButton` variants except `ICON_TEXT_SHORTCUT`, (it is `true` by default for the `InkeepSearchBar` and `InkeepCustomTrigger` components).
*   By default the `openShortcutKey` is `/` for the `InkeepChatButton` component (it is still `k` for the `InkeepSearchBar` and `InkeepCustomTrigger` components).

***

### Version 0.2.247

*   Added support for passing `colorMode` in `baseSettings`, passing in `colorMode` in `theme` is still supported but is deprecated.
*   Added support for passing `syntaxHighlighter` in `theme`, passing in `baseSettings` (as `highlighterTheme`) is still supported but is deprecated.
*   Changed the default `chatButtonType` to `ICON_TEXT`.

***

### Version 0.2.246

*   Added loading state for AI chat when passing in the chatId prop.

***

### Version 0.2.245

*   Added support for customizing the chat action button labels and switch to search button label.

***

### Version 0.2.244

*   Added a prop to `SearchSettings` called `submitOnEnterOnly` which will submit search query on enter instead of onChange. Default is `false`.

***

### Version 0.2.243

*   Added a prop to `AIChatSettings` called `chatId` which will load the chat corresponding to the id.

***

### Version 0.2.242

*   Bumped dependencies to address vulnerability in `vite@4.4.11`.

***

### Version 0.2.241

*   Fixed a bug where chat actions were only shown for most recent message.

***

### Version 0.2.238

*   Added a prop to `ModalSettings` called `shouldAnimateSwitchToChatButton` which determines whether to animate the switch to chat button on first load. Default is `true`.
*   Added a variant to the theme for the `SwitchToChat` button, example:

```js
const inkeepBaseSettings: InkeepBaseSettings = {
  theme: {
    components: {
      SwitchToChat: {
        defaultProps: {
          variant: "subtle", // options are 'primary' or 'subtle'
        },
      },
    },
  },
};
```


# Onboarding sources



Our goal is to make it effortless to create a knowledge base for search and chat experiences. With our integrations and intellegent crawlers, we automatically index, chunk, embed, and re-rank all types of different content to get the best search and chat retrieval performance (learn more [here](../overview/why-inkeep)).

## Onboarding new content

Send us a message over Slack or send an email to [sources@inkeep.com](mailto:founders@inkeep.com?subject=Adding%20a%20new%20source%20to%20Inkeep). We personally onboard and vet the quality of all new sources.

## Common sources

Below are sources that we support out of the box. Note that this list is *not exhaustive*, we're generally able to ingest any source.

### Documentation

*   Document360
*   Docusaurus
*   Next.js (Nextra and custom)
*   Astro (Starlight and custom)
*   Mintlify
*   ReadMe
*   MkDocs
*   GitBook
*   Redocly
*   VitePress / VuePress
*   Sphinx
*   Read the docs
*   Hugo
*   Gatsby
*   Custom

### Community

*   Discourse
*   Slack threads
*   Discord threads

### Marketing / blogs / landing pages

*   Webflow
*   Framer
*   Next.js
*   Astro
*   Custom

### CMS / Blogs

*   Wordpress
*   Sanity
*   Prismic
*   DatoCMS
*   Contentful
*   Strapi
*   Agility CMS
*   Cosmic
*   ButterCMS
*   Storyblok
*   Hygraph
*   Sitecore
*   Custom

### Technical

*   GitHub (pull requests, issues, readmes, releases)
*   Changelogs
*   GraphQL specifications
*   OpenAPI specifications

## Enterprise Sources

We support ingestion of historical support tickets, internal documents, and others sources that contains knowledge about your product. We'll work with your team to ingest any source in any format and your security team to ensure compliance with your data processing policies.

#### Help center and support tickets

*   Intercom
*   Zendesk
*   Help Scout
*   Freshdesk

#### Internal knowledge

*   Notion
*   Google Drive
*   Confluence
*   Files (.docx, .md, .csv, .pdf, etc.)


# Updating content

How content is kept up to date for any source

## Automatic re-indexing

We automatically re-index all content **every 24 hours**. We calculate differences in content and update all downstream dependencies as needed.

## Update with a webhook call

To update content on-demand, you can make an API call to Inkeep's management service that will trigger a source synchronization job. This is useful for content that has a CI/CD pipeline or for CMS systems that offer webhooks when content changes. [Contact us](mailto:sources@inkeep.com?subject=Access%20to%20webhook%20for%20content%20updates) for a management API key and guidance on how to set this up.

## Update with a GitHub Action

You can set up a GitHub Action workflow to automatically trigger Inkeep to re-crawl a source that corresponds to a (public or private) GitHub repo. This way of triggering indexing is possible for publicly available web content managed via Git, like documentation or marketing sites.

<Steps>
  <Step title="Add the Inkeep API key">
    1.  Get an Inkeep Management API key by contacting [help@inkeep.com](mailto:help@inkeep.com).
    2.  Under your repository settings, navigate to **Secrets and variables** -> **Actions**. Click on **New repository secret** and add your Inkeep API key with the name `INKEEP_API_KEY`.
  </Step>

  <Step title="Get the Source ID">
    1.  Go to the [Inkeep Dashboard](https://portal.inkeep.com)
    2.  Navigate to **Sources**
    3.  Selecting the source and copy the `ID`.
  </Step>

  <Step title="Create the Workflow">
    1.  In your GitHub repository, navigate to the **Actions** tab and click on **New workflow**.

    2.  Choose the **Set up a workflow yourself** option.

    3.  Insert the below into the workflow file:

    <CodeGroup>
      ```yml All files
      name: Sync Inkeep Source

      on:
        push:
          branches:
            - main

      jobs:
        syncSourceJob:
          runs-on: ubuntu-latest
          permissions:
            contents: read
            pull-requests: write

          steps:
          - name: Checkout
            uses: actions/checkout@v3
            
          - name: Sync Source
            uses: inkeep/pr-commenter-action@v10
            env:
              GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
            with:
              apiKey: ${{secrets.INKEEP_API_KEY}}
              sourceId: "{SOURCE_ID}"
      ```

      ```yml Specific Folder
      name: Inkeep Source Sync

      on:
        push:
          branches:
            - main
          paths:
            - "docs/**"

      jobs:
        syncSourceJob:
          runs-on: ubuntu-latest
          permissions:
            contents: read
            pull-requests: write

          steps:
            - name: Checkout
              uses: actions/checkout@v3
            - name: Check for changes
              uses: dorny/paths-filter@v2
              id: changes
              with:
                filters: |
                  docs:
                    - 'docs/**'
            - name: Sync Source
              if: steps.changes.outputs.docs == 'true'
              uses: inkeep/pr-commenter-action@v10
              env:
                GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
              with:
                apiKey: ${{secrets.INKEEP_API_KEY}}
                sourceId: "{SOURCE_ID}"
      ```
    </CodeGroup>

    4.  Replace `{SOURCE_ID}` with the ID of your Inkeep source.

    5.  Commit the workflow file to your repository.
  </Step>
</Steps>

### How it Works

*   The workflow is triggered whenever changes are pushed to the `main` branch and there are changes in the matching paths. Note that this only happens when a PR is merged or push is made to the target branch.

*   If the push to `main` is associated with a pull request, a comment will be added to the PR confirming that an ingestion job was triggered:

    :mag\_right: :speech\_balloon: [Inkeep](https://inkeep.com) AI search and chat service is syncing content for source '\{Source-Name}'

*   The new content will be ingested within **1 hour**.

### Permissions

| Permission             | Reason                                                                                                                                               |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contents: read`       | Allows the workflow to read the content of the repository, which is needed to detect changes in the content.                                         |
| `pull-requests: write` | Allows the workflow to write a comment on the PR when the workflow succeeds. Note that this only happens once the PR is merged to the target branch. |


# Copilot for Support Agents (Any Platform)

Keep, Inkeep's copilot for support agents, can be used with any support platform as a Chrome Extension.

<Snippet file="copilot/Overview.mdx" />

<Snippet file="copilot/GetApiKey.mdx" />

## Install using Chrome Extension

1.  **Access Chrome**: Open your Chrome or Chromium-based browser.

2.  **Install the Extension**: Go to the [Chrome Web Store Listing](https://chromewebstore.google.com/detail/keep-ai-copilot-for-suppo/pkhhiabkpgkjabdlakafhdmapkgdafkh).

3.  **Add to Chrome**: Click **Add to Chrome**, then **Continue to install**, and finally **Add extension** to install the extension.

4.  **Launch the Extension**: Click on the extension icon in your toolbar to launch it.

5.  **Set Integration Key**: Set the Integration Key to the Chrome Extension Support Copilot API key that you generated previously.

6.  **Enable Keep**: Visit a supported page and click on **Smart Assist** to start using Keep.

7.  **Pin App**: Pin the extension to the toolbar for easy access by right-clicking the extension icon and selecting "Pin".

## Install using Arc

1.  **Access Arc**: Open your Arc browser.

2.  **Install the Extension**: Go to the [Chrome Web Store Listing](https://chromewebstore.google.com/detail/keep-ai-copilot-for-suppo/pkhhiabkpgkjabdlakafhdmapkgdafkh).

3.  **Add to Arc**: Click **Add to Chrome**, then **Add extension** to install it in Arc.

4.  **Pin the Extension**: Click the puzzle piece icon in the Arc sidebar, find the Keep extension, and pin it for easier access.

5.  **Launch Copilot**: Click the pinned Keep icon. It will open as a new tab named "Inkeep Support Copilot". (Note: It may take a couple of seconds on first launch.)

6.  **Set Integration Key**: In the Copilot tab, set the Integration Key to the Chrome Extension Support Copilot API key that you generated previously.

7.  **Use Split View**: While on your current tab, **option-click** (alt-click on Windows) on the Copilot tab to open it in split view.

8.  **Start Using**: You can now use the Copilot side by side with your current tab. Click on **Smart Assist** to begin.

## Compatability

Currently, the Chrome Extension works on:

*   Salesforce
*   GitHub Issues
*   Plain

To request support for additional platforms, just ask [support@inkeep.com](mailto:support@inkeep.com?subject=Add%20compatibility%20to%20Chrome%20Extension%20copilot). We can quickly add support.

## Example Usage

To use Keep with a GitHub issue:

1.  Open a GitHub issue in a given tab.
2.  Launch the extension.
3.  Click on **Smart Assist**.

<Snippet file="copilot/DraftFAQ.mdx" />


# Turn closed support tickets into FAQs



## Overview

The Inkeep Support Copilot allows you to close the loop and improve future AI responses by using the AI to draft FAQs from resolved support tickets. This guide will walk you through the process of using the Support Copilot and managing the generated FAQs.

## Prerequisites

Before you begin, ensure you have:

1.  Created a Support Copilot integration
2.  Configured your Zendesk app with the necessary API credentials

## Using the Support Copilot

### Open the AI Copilot

1.  Navigate to a resolved support ticket in your system (or a ticket with sufficient information to build an FAQ)
2.  Open the Inkeep AI Copilot for support from either the Zendesk App or the Chrome Extension

{/* <Note>
    Insert a screenshot here showing the location of the AI Copilot button or interface within the support ticket view.
</Note> */}

### Generate an FAQ

1.  In the AI Copilot interface, click on **Draft FAQ**
2.  Wait for the AI to draft the FAQ based on the support ticket

{/* <Note>
    Insert a screenshot here displaying the "Draft FAQ" button and the generated FAQ draft.
</Note> */}

### Review and Save the FAQ

1.  Read over the generated FAQ items carefully
2.  You may chat with the copilot to request modifications to revise the draft FAQ.
3.  If you're satisfied with the question and answer, click **Save to my knowledge base**
4.  You should see a checkmark indicating that the FAQ has been successfully saved

## Managing FAQs

### Access the FAQ Management Page

1.  Return to the Inkeep Dashboard
2.  Click on the **Tuning FAQs** button

{/* <Note>
    Insert a screenshot here showing the location of the "Tuning FAQs" button in the dashboard.
</Note> */}

### View and Edit FAQs

On the FAQ management page, you can:

1.  View all generated FAQs
2.  Enable or disable individual FAQs
3.  Edit any fields of the FAQ
4.  See the sources used to draft the FAQ, including the original ticket source

{/* <Note>
    Insert a screenshot here of the FAQ management page, highlighting the key features mentioned above.
</Note> */}

## Using Support Copilot with Internal Projects

You can use the Support Copilot process with internal projects to maintain privacy for sensitive information:

1.  Create FAQs from internal support tickets
2.  Use these FAQs to improve AI-drafted responses for your support agents
3.  Keep sensitive information private while enhancing your AI performance

{/* <Note>
    This approach ensures that confidential data remains secure while still improving the AI's ability to assist your support team.
</Note> */}

## Benefits of Using Support Copilot

*   Continuously refine your AI's understanding of common issues and solutions
*   Improve the accuracy and relevance of AI-generated responses over time
*   Streamline the process of creating and managing FAQs from support interactions
*   Enhance the knowledge base available to both AI and human support agents

By regularly using the Support Copilot to generate FAQs from resolved tickets, you can create a feedback loop that consistently improves your AI's performance in handling support queries.


# Copilot for Support Agents (Zendesk)

Install Keep, Inkeep's Copilot for Support Agents, to your Zendesk workspace.

<Snippet file="copilot/Overview.mdx" />

<Snippet file="copilot/GetApiKey.mdx" />

## Install the Zendesk App

1.  **Access Zendesk**: Log in to your Zendesk account with administrator credentials.

2.  **Install the Application**: Go to the [App Listing](https://www.zendesk.com/marketplace/apps/support/1065305/keep-ai-copilot-for-support/) or navigate to the Zendesk Marketplace and search for "Keep AI Copilot for Support". Click the **Install** button to begin the installation process.

3.  **Authorize Installation**: Review and accept the requested permissions.

4.  **Configure the App**: Follow the prompts to set up the app, including inputting your Inkeep Support Copilot API key that you generated in the previous step.

5.  **Optimize Display**: Pin the app to the support sidebar for the best chat experience.

## Use with a ticket

To use Keep with a support ticket:

1.  Open a support ticket in Zendesk
2.  Open the Inkeep app on the right side bar
3.  Click on **Smart Assist**

<Snippet file="copilot/DraftFAQ.mdx" />

<Snippet file="copilot/ChangeApiKey.mdx" />


# Auto-reply to live chat with Inkeep AI



## Overview

Inkeep's Live Chat AI Agent integrates with Zendesk Messaging to provide intelligent, real-time responses to customer inquiries. The AI agent can handle common questions, provide product information, and escalate complex issues to human agents when needed.

## Demo

Try out our live demo to see the AI agent in action:

[Launch Demo](https://zendesk-copilot.inkeep.com/demo/inkeep)

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/inkeep/images/support-tools/zendesk-live-chat-demo.png" alt="Zendesk Live Chat AI Agent Demo" style={{maxWidth: "200px"}} />
</Frame>

## Key Features

*   **Real-time AI Responses**: Instantly responds to customer inquiries using your knowledge base
*   **Seamless Human Handoff**: Automatically transfers complex conversations to human agents
*   **Knowledge Base Integration**: Leverages your existing documentation to provide accurate answers
*   **Conversation History**: Maintains context throughout the chat for natural interactions
*   **Custom Branding**: Matches your brand's look and feel
*   **Analytics & Insights**: Track performance metrics and conversation quality

## Benefits

*   **24/7 Availability**: Provide instant support around the clock
*   **Reduced Wait Times**: Handle multiple conversations simultaneously
*   **Consistent Responses**: Ensure accurate and standardized information
*   **Agent Efficiency**: Free up human agents for complex issues
*   **Cost Effective**: Scale support operations without proportionally increasing costs

## Getting Started

To implement the Live Chat AI Agent:

1.  Contact our [sales team](mailto:sales@inkeep.com) to get started
2.  Provide your Zendesk credentials and configure integration settings
3.  Customize the AI agent's responses and behavior
4.  Test the integration in a sandbox environment
5.  Deploy to production

For detailed implementation guidance or questions, reach out to our [support team](mailto:support@inkeep.com).


# Auto-reply to support tickets with Inkeep AI



## Overview

This guide provides instructions for how to setup an AI auto-reply with Zendesk. The [template repo](https://github.com/inkeep/zendesk-inkeep-template) can be adapted to work with any support platform.

By default, the auto-responder implementation:

*   Provides an AI generated answer if it is `very_confident`
*   Leaves an internal note if it is not confident

The [webhook](https://github.com/inkeep/zendesk-inkeep-template/blob/main/src/app/api/webhook/route.ts) logic can be customized to your requirements. For example, you can customize when to bypass the AI autoresponse based on ticket or user attributes, add your own disclaimer language, and in general control the auto-reply flow.

## Prerequisites

Ensure you have:

1.  A Zendesk account with admin access
2.  Get an [Inkeep API key](https://docs.inkeep.com/ai-api/chat-completions-api#get-an-api-key)
3.  Access to deploy an API route (e.g., using Vercel Serverless Functions)

## 1. Deploy the webhook

The fastest way to get started is to deploy the serverless API route to Vercel:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Finkeep%2Fzendesk-inkeep-template)

Simply configure these environment variables:

| Variable            | Description                                                                                                                                                                       |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ZENDESK_SUBDOMAIN` | Your Zendesk subdomain (e.g., if your Zendesk URL is mycompany.zendesk.com, enter 'mycompany')                                                                                    |
| `ZENDESK_API_TOKEN` | Your Zendesk API token ([generate from Zendesk Admin settings](https://support.zendesk.com/hc/en-us/articles/4408889192858-Managing-access-to-the-Zendesk-API#topic_bsw_lfg_mmb)) |
| `ZENDESK_API_USER`  | Email address of the Zendesk user that generated the API token                                                                                                                    |
| `INKEEP_API_KEY`    | Your Inkeep API key                                                                                                                                                               |
| `AI_AGENT_USER_ID`  | (Optional) (Recommended) Numeric ID of the user you want to send messages as. Recommended to use an email address that you own and has the name "AI Agent" or similar             |
| `INTERNAL_ONLY`     | (Optional) (Recommended) true or false. If true, the AI will only add internal notes and not send a public response.                                                              |

## 2. Configure your local environment variables

Copy the `.env.sample` file to `.env` and configure the missing variables with the values you used in the previous step.
These variables are used by the scripts you will run in the next step.

## 3. Configure Zendesk

Now, let's configure Zendesk to send a webhook to your API route when new tickets are created.

### Option A: Automated (Recommended)

1.  Navigate to the [webhook\_setup folder](https://github.com/inkeep/zendesk-inkeep-template/tree/main/webhook_setup) in your local terminal (`cd webhook_setup`).
2.  Enable execution of the setup script by running `chmod +x setup.sh`.
3.  Execute it with `./setup.sh`. This will create the webhook and trigger for you.

The script will write the created webhook and trigger IDs to a file named `.webhook_resources`. You can use these IDs later to clean up the resources by running the cleanup script: `chmod +x cleanup.sh` and `./cleanup.sh`.

<Accordion title="Option B: Manual (Alternative)">
  1.  In Zendesk Admin Center, go to **Apps** and **Integrations** > **Webhooks**
  2.  Create a new webhook with the following settings:
      *   Name: `Inkeep Smart Replies`
      *   Endpoint URL: Your deployed Vercel URL + `/api/webhook` (e.g., `https://your-app.vercel.app/api/webhook`)
      *   Request method: `POST`
      *   Request format: `JSON`
      *   Basic authentication: `No`
      *   Active: `Yes`
  3.  Create a trigger to invoke the webhook:
      *   Go to **Admin Center** > **Objects and rules** > **Business rules** > **Triggers**
      *   Click **Add trigger**
      *   Name it `Inkeep Smart Reply Trigger`
      *   Set conditions:
      *   **Ticket**: Is Created
      *   Set actions:
      *   **Notify webhook**: Select the webhook you created
</Accordion>

## 4. Test the Setup

To verify everything is working correctly:

1.  Create a ticket:
    *   Go to your Zendesk workspace
    *   Create and submit a ticket with a test message

2.  Verify answer:

*   Check that an AI-generated response was added as a reply or internal note
*   The note should be from the user specified in your `AI_AGENT_USER_ID`

<Accordion title="Troubleshooting Zendesk Auto-reply setup">
  **Review the Zendesk webhook activity:**

  *   In Zendesk Admin Center, go to **Apps** and **Integrations** > **Webhooks**
  *   Click on your **Inkeep Smart Replies** webhook
  *   Check the **Activity** tab to confirm the webhook fired for your test ticket
  *   Verify the webhook shows a successful (200) response

  **Check the Vercel logs:**

  *   Go to your Vercel dashboard
  *   Select your deployed application
  *   Navigate to **Deployments** > **Latest Deployment** > **View Functions Logs**
  *   Look for logs showing the webhook request was received and processed

  **Check that your Zendesk Workspace is configured to send emails on public replies to tickets:**

  *   Look at the Zendesk email tigger guide [here.](https://support.zendesk.com/hc/en-us/articles/4408823742618-My-customers-don-t-receive-emails)

  **If your auto-responder is timing out:**

  *   The default maxDuration on Vercel free plan is 60 seconds. This is the maximum time the AI can take to generate a response. You can increase this by setting the `maxDuration` parameter on the route.ts file and upgrading to a paid Vercel plan.

  **Double-check the environment variables are configured correctly**
</Accordion>


# AI Chat

Customize the chat experience for the Inkeep widgets.

<Snippet file="InkeepAIChatSettings.mdx" />


# Base

Review the basic configuration settings for our search and chat experiences.

<Snippet file="InkeepBaseSettings.mdx" />


# Modal

Customize the modal component for Inkeep widgets.

<Snippet file="InkeepModalSettings.mdx" />


# Search

Customize search-related settings for the Inkeep widgets.

<Snippet file="InkeepSearchSettings.mdx" />


# Chat Button (JS)

Add the Inkeep chat button using a JS snippet.

The chat button is a great way to add an AI copilot to your landing page, documentation, or app without changing your current search experience.

<Snippet file="img-examples/ChatButtonExamples.mdx" />

## Quick start

<Snippet file="InkeepJSSnippet.mdx" />

Insert the Chat Button by using the `Inkeep.embed()` function.

```typescript
<script type="module" defer>
  const inkeepWidget = Inkeep().embed({
    componentType: "ChatButton", // required
    // optional -- for syncing UI color mode
    colorModeSync: {
      observedElement: document.documentElement,
      isDarkModeCallback: (el) => {
        return el.classList.contains("dark");
      },
      colorModeAttribute: 'class',
    },
    properties: {
      chatButtonType: 'PILL', // default. Alternatives are 'RECTANGLE_SHORTCUT' and 'ICON'
      baseSettings: {
        apiKey: process.env.INKEEP_API_KEY!, // required
        integrationId: process.env.INKEEP_INTEGRATION_ID!, // required
        organizationId: process.env.INKEEP_ORGANIZATION_ID!, // required
        organizationDisplayName: "Inkeep",
        primaryBrandColor: "#26D6FF",
        //... optional base settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
        },
      },
      modalSettings: {
        // optional InkeepModalSettings
      },
      searchSettings: {
        // optional InkeepSearchSettings
      },
      aiChatSettings: {
        // optional InkeepAIChatSettings
          botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
          botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode or more contrast against button bg
          quickQuestions: [
            "Example question 1?",
            "Example question 2?",
            "Example question 3?",
          ],
      }
    },
  });
</script>
```

## Inkeep.Embed() for Chat Button

| Parameter     | Type                    | Description                                                                                            |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------ |
| componentType | `string`                | **Required**. Must be `ChatButton` to add the ChatButton component.                                    |
| targetElement | `HTMLElement`           | A valid HTML element where you want to render the ChatButton component.                                |
| properties    | `InkeepChatButtonProps` | **Required**. An object that contains the base settings and other optional settings for the ChatButton |
| colorModeSync | `ColorModeSync`         | An object that contains the settings to sync the color mode in the widgets with the parent UI.         |

<Snippet file="integration_common/ColorModeSync.mdx" />

<Snippet file="InkeepChatButtonProps.mdx" />

<Snippet file="InkeepMultipleComponentsSettings.mdx" />

<Snippet file="InkeepRenderMethod.mdx" />


# Custom Trigger (JS)



Use the "Custom Trigger" component if you want to use a custom button, search bar, link, or any other UI element to open the Inkeep AI search or chat modal.

## Quick Start

JavaScript example:

```html
<script type="module" defer>
  // Get the button element
  const inkeepButton = document.getElementById("inkeepButton");

  // Create a new div element to hold the Inkeep modal and set its id and position
  const inkeepDiv = document.createElement("div");
  inkeepDiv.id = "inkeepModal";
  inkeepDiv.style.position = "absolute";
  document.body.appendChild(inkeepDiv);

  const handleClose = () => {
    inkeepWidget.render({
      ...config,
      isOpen: false,
    });
  };

  const handleOpen = () => {
    inkeepWidget.render({
      ...config,
      isOpen: true,
    });
  }

  const config = {
    componentType: "CustomTrigger", // required
    targetElement: inkeepDiv, // required
    properties: {
      isOpen: false, // required
      onClose: handleClose, // required
      onOpen: undefined,
      baseSettings: {
        apiKey: process.env.INKEEP_API_KEY!, // required
        integrationId: process.env.INKEEP_INTEGRATION_ID!, // required
        organizationId: process.env.INKEEP_ORGANIZATION_ID!, // required
        primaryBrandColor: "#26D6FF",
        organizationDisplayName: "Inkeep",
        //... optional base settings
      },
      modalSettings: {
        // optional InkeepModalSettings
      },
      searchSettings: {
        // optional InkeepSearchSettings
      },
      aiChatSettings: {
        // optional InkeepAIChatSettings
        botAvatarSrcUrl: "/img/inkeep-logo.svg",
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      },
    },
  };

  // Embed the widget using the `Inkeep.embed()` function.
  const inkeepWidget = Inkeep().embed(config);

  // Add event listener to open the Inkeep modal when the button is clicked
  inkeepButton.addEventListener("click", handleOpen);
</script>
```

HTML example:

```html
<!-- Other HTML code -->
<button id="inkeepButton">Open modal</button>
<!-- Other HTML code -->
```

<Snippet file="InkeepCustomTriggerProps.mdx" />


# Embedded Chat (JS)

Add a chat UI directly on a dedicated page

<Snippet file="embedded-chat/intro.mdx" />

## Quick start

<Steps>
  <Step>
    <Snippet file="InkeepJSSnippet.mdx" />
  </Step>

  <Step>
    Define an element in your page that will be the "container" for the embedded chat.

    ```html

    <div style="display: flex; align-items: center; justify-content: center; height: calc(100vh - 16px);">
      <div style="max-height: 600px; height: 100%;">
        <div id="ikp-embedded-chat-target"></div>
      </div> 
    </div>

    ```
  </Step>

  <Step>
    Insert the Embedded Chat by using the `Inkeep.embed()` function.

    ```typescript
    <script type="module" defer>
      const inkeepWidget = Inkeep().embed({
        componentType: "EmbeddedChat", // required
        targetElement: document.getElementById("ikp-embedded-chat-target"), // required
        properties: {
          // optional -- for syncing UI color mode
          colorModeSync: {
            observedElement: document.documentElement,
            isDarkModeCallback: (el) => {
              return el.classList.contains("dark");
            },
            colorModeAttribute: 'class',
          },
          baseSettings: {
            apiKey: process.env.INKEEP_API_KEY!, // required
            integrationId: process.env.INKEEP_INTEGRATION_ID!, // required
            organizationId: process.env.INKEEP_ORGANIZATION_ID!, // required
            organizationDisplayName: "Inkeep",
            primaryBrandColor: "#26D6FF",
            //... optional base settings
            theme: {
              // stylesheetUrls: ['/path/to/stylesheets'], // optional
            },
          },
          modalSettings: {
            // optional InkeepModalSettings
          },
          searchSettings: {
            // optional InkeepSearchSettings
          },
          aiChatSettings: {
            // optional InkeepAIChatSettings
            botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
            botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode or more contrast against button bg
            quickQuestions: [
              "Example question 1?",
              "Example question 2?",
              "Example question 3?",
            ],
          }
        },
      });
    </script>
    ```
  </Step>
</Steps>

## Styling

## Inkeep.Embed() for Embedded Chat

| Parameter     | Type                      | Description                                                                                               |
| ------------- | ------------------------- | --------------------------------------------------------------------------------------------------------- |
| componentType | `string`                  | **Required**. Must be `EmbeddedChat` to add the EmbeddedChat component.                                   |
| targetElement | `HTMLElement`             | **Required**. A valid HTML element where you want to render the EmbeddedChat component.                   |
| properties    | `InkeepEmbeddedChatProps` | **Required**. An object that contains the base settings and other optional settings for the Embedded Chat |
| colorModeSync | `ColorModeSync`           | An object that contains the settings to sync the color mode in the widgets with the parent UI.            |

<Snippet file="integration_common/ColorModeSync.mdx" />

<Snippet file="InkeepEmbeddedChatProps.mdx" />

<Snippet file="InkeepMultipleComponentsSettings.mdx" />

<Snippet file="InkeepRenderMethod.mdx" />


# Search Bar (JS)

Add the Inkeep search bar using a JS snippet.

<Snippet file="img-examples/SearchBarExamples.mdx" />

## Quick start

<Snippet file="InkeepJSSnippet.mdx" />

Insert the Search Bar by using the `Inkeep.embed()` function.

```typescript
<script type="module" defer>
  const inkeepWidget = Inkeep().embed({
    componentType: "SearchBar", // required
    targetElement: document.getElementById("inkeep-placeholder"), // required
    properties: {
      // optional -- for syncing UI color mode
      colorModeSync: {
        observedElement: document.documentElement,
        isDarkModeCallback: (el) => {
          return el.classList.contains("dark");
        },
        colorModeAttribute: 'class',
      },
      baseSettings: {
        apiKey: process.env.INKEEP_API_KEY!, // required
        integrationId: process.env.INKEEP_INTEGRATION_ID!, // required
        organizationId: process.env.INKEEP_ORGANIZATION_ID!, // required
        organizationDisplayName: "Inkeep",
        primaryBrandColor: "#26D6FF",
        //... optional base settings
        theme: {
          // stylesheetUrls: ['/path/to/stylesheets'], // optional
        },
      },
      modalSettings: {
        // optional InkeepModalSettings
      },
      searchSettings: {
        // optional InkeepSearchSettings
      },
      aiChatSettings: {
        // optional InkeepAIChatSettings
        botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
        botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode or more contrast against button bg
        quickQuestions: [
          "Example question 1?",
          "Example question 2?",
          "Example question 3?",
        ],
      }
    },
  });
</script>
```

## Inkeep.Embed() for Search Bar

| Parameter     | Type                   | Description                                                                                            |
| ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
| componentType | `string`               | **Required**. Must be `SearchBar` to add the SearchBar component.                                      |
| targetElement | `HTMLElement`          | **Required**. A valid HTML element where you want to render the SearchBar component.                   |
| properties    | `InkeepSearchBarProps` | **Required**. An object that contains the base settings and other optional settings for the Search Bar |
| colorModeSync | `ColorModeSync`        | An object that contains the settings to sync the color mode in the widgets with the parent UI.         |

<Snippet file="integration_common/ColorModeSync.mdx" />

<Snippet file="InkeepSearchBarProps.mdx" />

<Snippet file="InkeepMultipleComponentsSettings.mdx" />

<Snippet file="InkeepRenderMethod.mdx" />


# UI Kit Components



With Inkeep, you can easily embed your AI assistants anywhere your users have questions.

`uikit` is our component library that are embeddable as both React components or JS snippets.

## Components

<Snippet file="UiKitOptions.mdx" />

## Customization

Here are some common scenarios:

*   **[Customize theme & CSS](/customization-guides/style-components)** - Customize the look and feel of the UI components.
*   **[Add guidance & context](/customization-guides/product-workflows)** - Provide user-specific information or customize LLM behavior.
*   **[Manage user privacy preferences](/customization-guides/manage-user-preferences/javascript)** - Handle user privacy settings and analytics opt-outs.
*   **[Authenticate users](/customization-guides/authenticate-users)** - Limit chat or search usage to users authenticated with your app.
*   **[Log events to your own analytics](/customization-guides/use-your-own-analytics)** - Log events to your own custom analytics system.
*   **[Embed a support form](/customization-guides/embedded-support-form)** - Add a support form embedded within the chat experience.
*   **[Make chats sharable](/customization-guides/sharable-chats)** - Enable the 'Share' button to make chats sharable.


# Chat Button (React)

Add a chat button React component that triggers the Inkeep modal.

The chat button is a great way to add an AI copilot to your landing page, documentation, or app without changing your current search experience.

<Snippet file="img-examples/ChatButtonExamples.mdx" />

<Snippet file="QuickStartReactComponent.mdx" />

```typescript
import {
  InkeepChatButton,
  type InkeepChatButtonProps,
  type InkeepBaseSettings,
} from "@inkeep/uikit";

const baseSettings: InkeepBaseSettings = {
  apiKey: process.env.INKEEP_API_KEY!,
  integrationId: process.env.INKEEP_INTEGRATION_ID!,
  organizationId: process.env.INKEEP_ORGANIZATION_ID!,
  organizationDisplayName: "Inkeep",
  primaryBrandColor: "#26D6FF",
};

const InkeepChatButtonProps: InkeepChatButtonProps = {
  chatButtonType: 'PILL', // default. Alternatives are 'RECTANGLE_SHORTCUT' and 'ICON'
  baseSettings: {
    ...baseSettings,
  },
  modalSettings: {
    // optional typeof InkeepModalSettings
  },
  searchSettings: {
    // optional typeof InkeepSearchSettings
  },
  aiChatSettings: {
    // optional typeof InkeepAIChatSettings
    botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
    botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode or more contrast against button bg
    quickQuestions: [
      "Example question 1?",
      "Example question 2?",
      "Example question 3?",
    ],
  },
};

export const ChatButton = () => {
  return <InkeepChatButton {...InkeepChatButtonProps} />;
};
```

<Snippet file="InkeepChatButtonProps.mdx" />


# Custom Trigger (React)



Use the "Custom Trigger" component if you want to use a custom button, search bar, link, or any other UI element to open the Inkeep AI search or chat modal.

## Quick Start

```typescript
import React, { useState } from "react";
import {
  InkeepCustomTrigger,
  InkeepCustomTriggerProps,
  InkeepBaseSettings,
} from "@inkeep/uikit";

const baseSettings: InkeepBaseSettings = {
  apiKey: process.env.INKEEP_API_KEY!,
  integrationId: process.env.INKEEP_INTEGRATION_ID!,
  organizationId: process.env.INKEEP_ORGANIZATION_ID!,
  organizationDisplayName: "Inkeep",
  primaryBrandColor: "#26D6FF",
};

const InkeepCustomTriggerComponent: React.FC = () => {
  const [isOpen, setIsOpen] = useState(false);

  const handleClose = useCallback(() => {
    console.log("Modal closed");
    setIsOpen(false);
  }, []);

  const inkeepCustomTriggerProps: InkeepCustomTriggerProps = {
    isOpen,
    onClose: handleClose,
    baseSettings,
  };

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
      <InkeepCustomTrigger {...inkeepCustomTriggerProps} />
    </div>
  );
};

export default InkeepCustomTriggerComponent;
```

<Snippet file="InkeepCustomTriggerProps.mdx" />

#### Example

```typescript
import type { InkeepCustomTriggerProps } from "@inkeep/uikit";
import baseSettings from "./baseSettings"; // your base settings typeof InkeepBaseSettings

const modalSettings: InkeepCustomTriggerProps = {
    isOpen: false,
    onClose: undefined,
    onOpen: undefined,
    baseSettings : {
        ...baseSettings,
    },
    modalSettings : {
        //... typeof InkeepModalSettings
    }
    searchSettings : {
        //... typeof InkeepSearchSettings
    },
    aiChatSettings : {
        //... typeof InkeepAIChatSettings
    },
};
```


# Embedded Chat (React)

Add a chat UI directly on a dedicated React powered page.

<Snippet file="embedded-chat/intro.mdx" />

<Snippet file="QuickStartReactComponent.mdx" />

```typescript
import {
  InkeepEmbeddedChat,
  type InkeepEmbeddedChatProps,
} from "@inkeep/uikit";

const baseSettings: InkeepBaseSettings = {
  apiKey: process.env.INKEEP_API_KEY!,
  integrationId: process.env.INKEEP_INTEGRATION_ID!,
  organizationId: process.env.INKEEP_ORGANIZATION_ID!,
  organizationDisplayName: "Inkeep",
  primaryBrandColor: "#26D6FF",
};

const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  baseSettings: {
    ...baseSettings,
  },
  aiChatSettings: {
    // ... typeof InkeepAIChatSettings
    botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
    botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode or more contrast against button bg
    quickQuestions: [
      "Example question 1?",
      "Example question 2?",
      "Example question 3?",
    ],
  },
};

export const EmbeddedChat = () => {
  return (
    <div>
      <InkeepEmbeddedChat {...inkeepEmbeddedChatProps} />
    </div>
  );
};
```

<Snippet file="InkeepEmbeddedChatProps.mdx" />

### Example

```typescript
import type { InkeepEmbeddedChatProps } from "@inkeep/uikit";
import baseSettings from "./baseSettings"; // your base settings typeof InkeepBaseSettings

const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
  shouldAutoFocusInput: true,
  baseSettings: {
    ...baseSettings,
  },
  aiChatSettings: {
    //... typeof InkeepAIChatSettings
  },
};
```


# React 17

Using the uikit with React 17.

The `@inkeep/uikit` package supports React 18, in order to use the Inkeep components with React 17 you can install the `@inkeep/uikit-js` package.

## Quick Start

<Snippet file="InstallTheJSWidgetLibrary.mdx" />

### Define the component

#### Chat Button

See [here](/ui-components/js-snippet/chat-button#inkeep-embed-for-chat-button) for additional supported props.

```js
import { useEffect, useRef } from "react";
import * as inkeepJS from "@inkeep/uikit-js";

const baseSettings = {
  integrationId: process.env.INKEEP_API_KEY,
  apiKey: process.env.INKEEP_INTEGRATION_ID,
  organizationId: process.env.INKEEP_ORGANIZATION_ID,
  primaryBrandColor: "#000000",
};

export const ChatButton = () => {
  const chatButtonRef = useRef(null);

  useEffect(() => {
    const inkeep = inkeepJS.Inkeep(baseSettings);
    chatButtonRef.current = inkeep.embed({
      componentType: "ChatButton",
      targetElement: "#chat-button",
      properties: {
        chatButtonType: "PILL",
        baseSettings,
        aiChatSettings: {
          quickQuestions: ["How to get started?"],
        },
      },
    });
  }, []);

  return <div id="chat-button" />;
};
```

#### Search Bar

See [here](/ui-components/js-snippet/search-bar#inkeep-embed-for-search-bar) for additional supported props.

```js
import { useEffect, useRef } from "react";
import * as inkeepJS from "@inkeep/uikit-js";

const baseSettings = {
  integrationId: process.env.INKEEP_API_KEY,
  apiKey: process.env.INKEEP_INTEGRATION_ID,
  organizationId: process.env.INKEEP_ORGANIZATION_ID,
  primaryBrandColor: "#000000",
};

export const SearchBar = () => {
  const SearchBarRef = useRef(null);

  useEffect(() => {
    const inkeep = inkeepJS.Inkeep(baseSettings);

    SearchBarRef.current = inkeep.embed({
      componentType: "SearchBar",
      targetElement: "#search-bar-target",
      properties: {
        baseSettings,
        aiChatSettings: {
          quickQuestions: ["How to get started?"],
        },
        // optional -- for syncing UI color mode
        // colorModeSync: {
        //   observedElement: document.documentElement,
        //   isDarkModeCallback: (el) => {
        //     return el.classList.contains("dark");
        //   },
        //   colorModeAttribute: "class",
        // },
      },
    });
  }, []);

  return <div id="search-bar-target" />;
};
```

#### Embedded Chat

See [here](/ui-components/js-snippet/embedded-chat#inkeep-embed-for-embedded-chat) for additional supported props.

```js
import { useEffect, useRef } from "react";
import * as inkeepJS from "@inkeep/uikit-js";

const baseSettings = {
  integrationId: process.env.INKEEP_API_KEY,
  apiKey: process.env.INKEEP_INTEGRATION_ID,
  organizationId: process.env.INKEEP_ORGANIZATION_ID,
  primaryBrandColor: "#805eff",
};

export const EmbeddedChat = () => {
  const embeddedChatRef = useRef(null);

  useEffect(() => {
    const inkeep = inkeepJS.Inkeep(baseSettings);

    embeddedChatRef.current = inkeep.embed({
      componentType: "EmbeddedChat",
      targetElement: "#embedded-chat-target",
      properties: {
        baseSettings,
        aiChatSettings: {
          quickQuestions: ["How to get started?"],
        },
        // optional -- for syncing UI color mode
        // colorModeSync: {
        //   observedElement: document.documentElement,
        //   isDarkModeCallback: (el) => {
        //     return el.classList.contains("dark");
        //   },
        //   colorModeAttribute: "class",
        // },
      },
    });
  }, []);

  return (
    <div className="ikp-wrapper">
      <div id="embedded-chat-target" className="ikp-embedded-chat-wrapper" />
    </div>
  );
};
```

#### Custom Trigger

See [here](/ui-components/js-snippet/custom-trigger#inkeepcustomtriggerprops) for additional supported props.

```js
import { useEffect, useRef, useState } from "react";
import * as inkeepJS from "@inkeep/uikit-js";

const baseSettings = {
  integrationId: process.env.INKEEP_API_KEY,
  apiKey: process.env.INKEEP_INTEGRATION_ID,
  organizationId: process.env.INKEEP_ORGANIZATION_ID,
  primaryBrandColor: "#000000",
};

export const CustomTrigger = () => {
  const CustomTriggerRef = useRef(null);
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    const inkeep = inkeepJS.Inkeep(baseSettings);

    CustomTriggerRef.current = inkeep.embed({
      componentType: "CustomTrigger",
      properties: {
        baseSettings,
        aiChatSettings: {
          quickQuestions: ["How to get started?"],
        },
        isOpen,
        onClose: () => {
          setIsOpen(false);
        },
        // optional -- for syncing UI color mode
        // colorModeSync: {
        //   observedElement: document.documentElement,
        //   isDarkModeCallback: (el) => {
        //     return el.classList.contains("dark");
        //   },
        //   colorModeAttribute: "class",
        // },
      },
    });
  }, []);

  useEffect(() => {
    if (CustomTriggerRef.current) {
      CustomTriggerRef.current.render({ isOpen });
    }
  }, [isOpen]);

  return (
    <button type="button" onClick={() => setIsOpen(true)}>
      Ask AI
    </button>
  );
};
```

### Server side rendering

If you are using a framework that utilizes server side rendering you will need to import the library asynchronously within a useEffect hook.

#### Example

This example uses the Chat Button component but the same logic can be applied to any of the other components.

```js
import { useEffect, useRef } from "react";
import { inkeepProps } from "./inkeepSettings";

export const ChatButtonSSR = () => {
  const chatButtonRef = useRef(null);

  useEffect(() => {
    const loadInkeepEmbed = async () => {
      const inkeepEmbed = await import("@inkeep/uikit-js");

      const inkeepChatButtonProps = {
        chatButtonType: "PILL",
        ...inkeepProps,
      };

      const inkeep = inkeepEmbed.Inkeep(inkeepProps.baseSettings);

      chatButtonRef.current = inkeep.embed({
        componentType: "ChatButton",
        targetElement: "#chat-button",
        properties: inkeepChatButtonProps,
      });
    };

    loadInkeepEmbed();
  }, []);

  return <div id="chat-button" />;
};
```


# Search Bar (React)

Add the Inkeep search bar as a React component.

<Snippet file="img-examples/SearchBarExamples.mdx" />

<Snippet file="QuickStartReactComponent.mdx" />

```typescript
import {
  InkeepSearchBar,
  type InkeepSearchBarProps,
  type InkeepBaseSettings,
} from "@inkeep/uikit";

const baseSettings: InkeepBaseSettings = {
  apiKey: process.env.INKEEP_API_KEY!, // required
  integrationId: process.env.INKEEP_INTEGRATION_ID!, // required
  organizationId: process.env.INKEEP_ORGANIZATION_ID!, // required
  organizationDisplayName: "Inkeep",
  primaryBrandColor: "#26D6FF",
};

const searchBarProps: InkeepSearchBarProps = {
  baseSettings: {
    ...baseSettings,
    theme: {
      // stylesheetUrls: ['/path/to/stylesheets'], // optional
    },
  },
  modalSettings: {
    // optional InkeepModalSettings
  },
  searchSettings: {
    // optional InkeepSearchSettings
  },
  aiChatSettings: {
    // optional typeof InkeepAIChatSettings
    botAvatarSrcUrl: "/img/inkeep-logo.svg", // use your own bot avatar
    botAvatarDarkSrcUrl: "/img/inkeep-logo-dark.svg", // for dark mode
    quickQuestions: [
      "Example question 1?",
      "Example question 2?",
      "Example question 3?",
    ],
  },
};

export const SearchBar = () => {
  return <InkeepSearchBar {...searchBarProps} />;
};
```

<Snippet file="InkeepSearchBarProps.mdx" />