# 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. |
## 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 => (
}
>
)
}
```
## 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
```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()
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
>
)
}
```
# 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.
## 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);
```
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.
## 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=`. 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.
Prefer a visual walkthrough? Check out [this video](https://vimeo.com/1010449025/54aade446b?share=copy).
## 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.
## 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.
## 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**
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.
## 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. |
# JavaScript
Manage end-user usage tracking and privacy settings.
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.
By default, Inkeep does not collect IP addresses, device IDs, or other device-based information from components of the Inkeep `uikit` library.
## 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
```
# React
Manage end-user usage tracking and privacy settings.
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.
By default, Inkeep does not collect IP addresses, device IDs, or other device-based information from components of the Inkeep `uikit` library.
## 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) => {
setOptOutAnalytics(e.target.checked);
};
return (
<>
{/* ... */}
{/* ... */}
>
);
}
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>();
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 ? : <>>;
}
// 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).
## 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.
When clicked, it generates a link to a chat so a user can share with other users.
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.
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`.
## 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`.
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
```jsx React
```
```html HTML
```
##### With a `display='block'` parent
```jsx React
```
We recommend setting the height for the container div to `height: min(80vh,
800px);`
## 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
## 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 `` or `;
const inkeepEmbeddedChatProps: InkeepEmbeddedChatProps = {
baseSettings: {
// ... typeof InkeepBaseSettings
theme: {
stylesheets: [styleTag],
},
},
//...
};
```
### Hosting the stylesheet
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.{" "}
You can host your stylesheet in a CDN or include it as part of the public assets exposed in an existing application.
For example:
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"],
},
},
//...
};
```
#### 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"],
},
},
//...
};
```
### 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
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
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.
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
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.
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
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
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.
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.
# Features
Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).
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
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.
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.
While we love supporting open source teams and projects like Drizzle, Auth.js and others, our solution is not currently open source.
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.
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.
# Projects & Sources
Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).
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.
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.
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.
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.
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.
# Pricing
Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).
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.
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.
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.
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.
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.
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.
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.
We do not currently support multi-tenant or reseller/vendor scenarios.
# Troubleshooting
Can't find an answer? Reach out to [support@inkeep.com](mailto:support@inkeep.com?subject=Question).
If you're having trouble loading the Inkeep UI components, try checking the below.
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: ``
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
});
```
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.
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.
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`.
The Inkeep UI components and related scripts are loaded asynchronously so they
don't affect page performance.
# Add AI Chat to your Astro app
### Define the component
Next, create an `InkeepChatButton.tsx` file for the [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.
### Use the component
Now use the `InkeepChatButton.tsx` component in your Astro based website.
```jsx
// ...
// ...
```
Use the `client:load` directive to load and hydrate the `InkeepChatButton.tsx` component immediately on page load.
# Add AI search to your Astro app
### Define the component
Next, create an `InkeepSearchBar.tsx` file for the [`Search Bar`](https://docs.inkeep.com/ui-components/react/search-bar) component.
### Use the component
Now use the `InkeepSearchBar.tsx` component in your Astro based website.
```jsx
// ...
// ...
```
Use the `client:load` directive to load and hydrate the `InkeepSearchBar.tsx` component immediately on page load.
# Add AI Chat to your Bettermode community
## Overview
You can add AI chat as a [Bettermode](https://bettermode.com/) widget to your Bettermode community.
## 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:
{" "}
## 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 ` area:
```html
```
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.
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**
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.
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.
Unfortunately, Discord does not currently support custom avatars or icons for published bots.
## 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
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.
### 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.
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.
Need to make Discord SEO-friendly? Try [Answer Overflow](https://www.answeroverflow.com?utm=inkeep) ↗️ (partner).
# 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.
## 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:
## 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:
```html Base Theme
```
```html Material Design Theme
```
Optional: click **preview** to test out the widget.
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:
Go back to the main community page and reload the page.
# Add AI Chat to your Document360 docs
## 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();
});
});
```
# Add AI Search & Chat Button to your Document360 docs
## 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
```
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"));
});
});
```
# Add AI Search to your Document360 docs
## 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
```
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);
});
```
# 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.
## 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?",
],
},
},
},
```
# 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.
## 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?",
],
},
},
},
```
# 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.
## Define the widget
Аdd the search bar as a theme in your `docusaurus.config.js` file:
```js docusaurus.config.js
themes: ["@inkeep/docusaurus/searchBar"],
```
If you are already using `Algolia DocSearch` provided by Docusaurus by default, it will be replaced by our widget.
### С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?",
],
},
},
},
```
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.
# Add AI Chat to your Framer website
## Initialize the widget
Next, paste the script below into the **End of `` tag** section:
```html
```
## 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.
# Add AI Search to your Framer website
## 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
3. Add an html element to the page
4. Specify the ID of the container to be `inkeepSearchBar`
## Initialize the widget
Next, paste the script below into the **End of `` tag** section:
```html
```
## 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.
# Add Inkeep's UI components to your Fumadocs
## 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 (
```
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.
## 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
```
Depending on your Hugo theme, this step may differ. The goal is to be able to insert custom scripts into your sites `` tag.
### Load the script files
In the `baseof.html` file, locate the **head** tag and paste the scripts below at the end:
```html baseof.html
```
## 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?",
],
},
},
});
```
# Add AI Chat to your Docsy docs
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?",
],
},
},
});
```
# Add AI Search to your Docsy docs
Disable the default search in the `config.toml (or hugo.toml)` file, by setting the `offlineSearch` parameter to **false**.
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,
});
```
# 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.
## 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
);
});
```
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.
# Add AI Chat to your MkDocs docs
## 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
});
});
```
# Add AI Search & Chat Button to your MkDocs docs
## 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");
});
});
```
# Add AI Search to your MkDocs docs
## 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",
},
},
});
});
});
```
# Add AI Chat to your Next.js app
### Define the component
Next, create an `InkeepChatButton.tsx` file for our [`Chat Button`](https://docs.inkeep.com/ui-components/react/chat-button) component.
# Add a Custom Trigger to your Next.js app
Trigger the Inkeep modal with a custom button in your Next.js app.
### Define the component
Create an `InkeepCustomTrigger.tsx` file for our [`Custom Trigger`](https://docs.inkeep.com/ui-components/react/custom-trigger) component. `