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

# Set up conversational search

> Enable the chat completions feature, configure your indexes, and create a workspace to start using conversational search.

Before building a chat interface or generating summarized answers, you need to enable the feature, configure your indexes, and create a workspace. This setup is shared across all conversational search use cases.

## Enable the chat completions feature

Enable chat completions from your Meilisearch Cloud project in one of two ways:

* Go to your project's **Settings** page and enable it under **Experimental features**
* Or open the **Chat** tab in your project and activate the feature directly from there

<Note>
  For self-hosted instances, enable the feature through the [experimental features API](/reference/api/experimental-features/configure-experimental-features) by sending a `PATCH` request with `chatCompletions` set to `true`:

  <CodeSamplesUpdateExperimentalFeaturesChat1 />
</Note>

## Find your chat API key

Meilisearch automatically generates a "Default Chat API Key" that combines `chatCompletions` and `search` permissions on all indexes. Conversational search requires both actions: `chatCompletions` authorizes the LLM call, and `search` authorizes the retrieval step that feeds documents to the model. Any key you use with the `/chats` routes must carry both actions, so prefer the default chat API key unless you have a specific reason to create a custom one.

Check if you have the key using:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X GET 'MEILISEARCH_URL/keys' \
    -H 'Authorization: Bearer MASTER_KEY'
  ```

  ```javascript JS theme={null}
  const client = new MeiliSearch({ host: 'MEILISEARCH_URL', apiKey: 'masterKey' })
  client.getKeys()
  ```

  ```python Python theme={null}
  client = Client('MEILISEARCH_URL', 'masterKey')
  client.get_keys()
  ```

  ```php PHP theme={null}
  $client = new Client('MEILISEARCH_URL', 'masterKey');
  $client->getKeys();
  ```

  ```java Java theme={null}
  Client client = new Client(new Config("MEILISEARCH_URL", "masterKey"));
  client.getKeys();
  ```

  ```ruby Ruby theme={null}
  client = MeiliSearch::Client.new('MEILISEARCH_URL', 'masterKey')
  client.keys
  ```

  ```go Go theme={null}
  client := meilisearch.New("MEILISEARCH_URL", meilisearch.WithAPIKey("masterKey"))
  client.GetKeys(nil);
  ```

  ```csharp C# theme={null}
  MeilisearchClient client = new MeilisearchClient("MEILISEARCH_URL", "masterKey");
  var keys = await client.GetKeysAsync();
  ```

  ```rust Rust theme={null}
  let client = Client::new("MEILISEARCH_URL", Some("MASTER_KEY")); let keys = client .get_keys() .await .unwrap();
  ```

  ```swift Swift theme={null}
  client = try MeiliSearch(host: "MEILISEARCH_URL", apiKey: "masterKey")
  client.getKeys { result in
      switch result {
      case .success(let keys):
          print(keys)
      case .failure(let error):
          print(error)
      }
  }
  ```

  ```dart Dart theme={null}
  var client = MeiliSearchClient('MEILISEARCH_URL', 'masterKey');
  await client.getKeys();
  ```
</CodeGroup>

Look for the key with the description "Default Chat API Key".

### Restrict chat access to specific indexes

Chat queries only search the indexes that the API key can access. The default chat API key is scoped to all indexes. To limit which indexes a chat client can reach, you have two options:

* Create a new API key with both `chatCompletions` and `search` actions, scoped to the exact indexes you want exposed. See [manage API keys](/capabilities/security/how_to/manage_api_keys) for the full workflow.
* Generate a [tenant token](/capabilities/security/how_to/generate_token_from_scratch) from the default chat API key. Tenant tokens inherit both the `chatCompletions` and `search` actions from their parent key and let you narrow index access or attach search rules per user.

<Note>
  A tenant token cannot grant access to an index its parent API key does not already cover. Make sure the parent key is scoped to every index the token should be allowed to reach.
</Note>

### Troubleshooting: Missing default chat API key

If your instance does not have a Default Chat API Key, create one manually:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/keys' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "name": "Chat API Key",
      "description": "API key for chat completions",
      "actions": ["search", "chatCompletions"],
      "indexes": ["*"],
      "expiresAt": null
    }'
  ```
</CodeGroup>

## Configure your indexes

Configure the [chat settings](/reference/api/chats/update-settings-of-a-chat-workspace) for each index you want to make available to the conversational search agent:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/chat' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "description": "A movie database containing titles, genres, release dates, keywords, and plot overviews to help users find films to watch",
      "documentTemplate": "A movie titled '\''{{doc.title}}'\'' that released in {{ doc.release_date | date: '\''%Y'\'' }}. The movie genres are: {{doc.genres}}. The key themes include: {{doc.keywords}}. The storyline is about: {{doc.overview|truncatewords: 100}}",
      "documentTemplateMaxBytes": 400
    }'
  ```
</CodeGroup>

* `description` tells the LLM what the index contains. A good description helps the agent decide which index to search and improves answer relevance. See [optimize chat prompts](/capabilities/conversational_search/how_to/optimize_chat_prompts#write-a-good-index-description) for tips on writing effective descriptions
* `documentTemplate` is a [Liquid](https://shopify.github.io/liquid/) template that defines the text representation of each document sent to the LLM. Write it as natural language so the model can extract relevant information easily. Consult the [document template best practices](/capabilities/hybrid_search/advanced/document_template_best_practices) article for more guidance
* `documentTemplateMaxBytes` sets a size limit on the text generated from the template. If the rendered text exceeds this limit, it is truncated. The default of 400 bytes balances context quality and speed

You can also configure `searchParameters` to control how the LLM searches the index (hybrid search, result limits, sorting, etc.). See [configure index chat settings](/capabilities/conversational_search/how_to/configure_index_chat_settings) for all available options.

## Configure a workspace

A workspace holds your LLM provider configuration and system prompt. Each workspace can:

* Connect to a different LLM provider (OpenAI, Azure OpenAI, Mistral, vLLM, or any OpenAI-compatible provider)
* Define its own system prompt and conversation context
* Access a specific set of indexes

The specific model to use is chosen per request in the `/chat/completions` call, not in the workspace settings.

### On Meilisearch Cloud

Your project comes with a single default workspace named `cloud`. Use `cloud` as the `WORKSPACE_NAME` in all API calls:

<CodeGroup>
  ```bash OpenAI theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "openAi",
      "apiKey": "PROVIDER_API_KEY",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```

  ```bash Azure OpenAI theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "azureOpenAi",
      "apiKey": "PROVIDER_API_KEY",
      "baseUrl": "PROVIDER_API_URL",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```

  ```bash Mistral theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "mistral",
      "apiKey": "PROVIDER_API_KEY",
      "baseUrl": "PROVIDER_API_URL",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```

  ```bash vLLM theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "vLlm",
      "baseUrl": "PROVIDER_API_URL",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```
</CodeGroup>

If your use case requires multiple workspaces, contact us. This limit may change in the future.

### On self-hosted instances

You can create as many workspaces as you need. Choose any name for `WORKSPACE_NAME` — if the workspace does not exist, Meilisearch creates it automatically:

<CodeGroup>
  ```bash OpenAI theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "openAi",
      "apiKey": "PROVIDER_API_KEY",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```

  ```bash Mistral theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "mistral",
      "apiKey": "PROVIDER_API_KEY",
      "baseUrl": "PROVIDER_API_URL",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```

  ```bash vLLM theme={null}
  curl \
    -X PATCH 'MEILISEARCH_URL/chats/WORKSPACE_NAME/settings' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "source": "vLlm",
      "baseUrl": "PROVIDER_API_URL",
      "prompts": {
        "system": "You are a helpful assistant. Answer questions based only on the provided context."
      }
    }'
  ```
</CodeGroup>

`baseUrl` is required for all providers except OpenAI. For OpenAI, it is optional and only needed if you are using a custom endpoint. See the [workspace settings API reference](/reference/api/chats/update-settings-of-a-chat-workspace) for all available fields.

The `prompts.system` field gives the agent its baseline instructions. For guidance on writing effective prompts, see [configure guardrails](/capabilities/conversational_search/how_to/configure_guardrails) and [optimize chat prompts](/capabilities/conversational_search/how_to/optimize_chat_prompts).

## Next steps

Your conversational search setup is complete. Choose how you want to use it:

<CardGroup cols={2}>
  <Card title="Build a chat interface" href="/capabilities/conversational_search/getting_started/chat">
    Create a multi-turn conversational interface where users ask follow-up questions.
  </Card>

  <Card title="Generate summarized answers" href="/capabilities/conversational_search/getting_started/one_shot_summarization">
    Display concise AI-generated answers alongside traditional search results.
  </Card>
</CardGroup>
